ua_pubsub_ns0.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright (c) 2017-2018 Fraunhofer IOSB (Author: Andreas Ebner)
  6. */
  7. #include "ua_server_pubsub.h"
  8. #include "ua_types.h"
  9. #include "ua_types.h"
  10. #include "ua_pubsub_ns0.h"
  11. #include "ua_pubsub.h"
  12. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* conditional compilation */
  13. typedef struct{
  14. UA_NodeId parentNodeId;
  15. UA_UInt32 parentCalssifier;
  16. UA_UInt32 elementClassiefier;
  17. } UA_NodePropertyContext;
  18. static UA_StatusCode
  19. addPubSubObjectNode(UA_Server *server, char* name, UA_UInt32 objectid,
  20. UA_UInt32 parentid, UA_UInt32 referenceid, UA_UInt32 type_id) {
  21. UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
  22. object_attr.displayName = UA_LOCALIZEDTEXT("", name);
  23. return UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(0, objectid),
  24. UA_NODEID_NUMERIC(0, parentid),
  25. UA_NODEID_NUMERIC(0, referenceid),
  26. UA_QUALIFIEDNAME(0, name),
  27. UA_NODEID_NUMERIC(0, type_id),
  28. object_attr, NULL, NULL);
  29. }
  30. static UA_StatusCode
  31. writePubSubNs0VariableArray(UA_Server *server, UA_UInt32 id, void *v,
  32. size_t length, const UA_DataType *type) {
  33. UA_Variant var;
  34. UA_Variant_init(&var);
  35. UA_Variant_setArray(&var, v, length, type);
  36. return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var);
  37. }
  38. static UA_NodeId
  39. findSingleChildNode(UA_Server *server, UA_QualifiedName targetName,
  40. UA_NodeId referenceTypeId, UA_NodeId startingNode){
  41. UA_NodeId resultNodeId;
  42. UA_RelativePathElement rpe;
  43. UA_RelativePathElement_init(&rpe);
  44. rpe.referenceTypeId = referenceTypeId;
  45. rpe.isInverse = false;
  46. rpe.includeSubtypes = false;
  47. rpe.targetName = targetName;
  48. UA_BrowsePath bp;
  49. UA_BrowsePath_init(&bp);
  50. bp.startingNode = startingNode;
  51. bp.relativePath.elementsSize = 1;
  52. bp.relativePath.elements = &rpe;
  53. UA_BrowsePathResult bpr =
  54. UA_Server_translateBrowsePathToNodeIds(server, &bp);
  55. if(bpr.statusCode != UA_STATUSCODE_GOOD ||
  56. bpr.targetsSize < 1)
  57. return UA_NODEID_NULL;
  58. if(UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, &resultNodeId) != UA_STATUSCODE_GOOD){
  59. UA_BrowsePathResult_deleteMembers(&bpr);
  60. return UA_NODEID_NULL;
  61. }
  62. UA_BrowsePathResult_deleteMembers(&bpr);
  63. return resultNodeId;
  64. }
  65. static void
  66. onRead(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
  67. const UA_NodeId *nodeid, void *nodeContext,
  68. const UA_NumericRange *range, const UA_DataValue *data) {
  69. UA_Variant value;
  70. UA_Variant_init(&value);
  71. UA_NodeId myNodeId;
  72. UA_WriterGroup *writerGroup = NULL;
  73. switch(((UA_NodePropertyContext *) nodeContext)->parentCalssifier){
  74. case UA_NS0ID_PUBSUBCONNECTIONTYPE:
  75. break;
  76. case UA_NS0ID_WRITERGROUPTYPE:
  77. myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId;
  78. writerGroup = UA_WriterGroup_findWGbyId(server, myNodeId);
  79. if(!writerGroup)
  80. return;
  81. switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier){
  82. case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL:
  83. UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]);
  84. break;
  85. default:
  86. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown property.");
  87. }
  88. break;
  89. default:
  90. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown parent element.");
  91. }
  92. UA_Server_writeValue(server, *nodeid, value);
  93. }
  94. static void
  95. onWrite(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
  96. const UA_NodeId *nodeId, void *nodeContext,
  97. const UA_NumericRange *range, const UA_DataValue *data){
  98. UA_Variant value;
  99. UA_NodeId myNodeId;
  100. UA_WriterGroup *writerGroup = NULL;
  101. switch(((UA_NodePropertyContext *) nodeContext)->parentCalssifier){
  102. case UA_NS0ID_PUBSUBCONNECTIONTYPE:
  103. break;
  104. case UA_NS0ID_WRITERGROUPTYPE:
  105. myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId;
  106. writerGroup = UA_WriterGroup_findWGbyId(server, myNodeId);
  107. UA_WriterGroupConfig writerGroupConfig;
  108. memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
  109. if(!writerGroup)
  110. return;
  111. switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier){
  112. case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL:
  113. UA_Server_getWriterGroupConfig(server, writerGroup->identifier, &writerGroupConfig);
  114. writerGroupConfig.publishingInterval = *((UA_Duration *) data->value.data);
  115. UA_Server_updateWriterGroupConfig(server, writerGroup->identifier, &writerGroupConfig);
  116. UA_Variant_setScalar(&value, data->value.data, &UA_TYPES[UA_TYPES_DURATION]);
  117. UA_WriterGroupConfig_deleteMembers(&writerGroupConfig);
  118. break;
  119. default:
  120. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Write error! Unknown property element.");
  121. }
  122. break;
  123. default:
  124. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown parent element.");
  125. }
  126. }
  127. static UA_StatusCode
  128. addVariableValueSource(UA_Server *server, UA_ValueCallback valueCallback,
  129. UA_NodeId node, UA_NodePropertyContext *context){
  130. UA_Server_setNodeContext(server, node, context);
  131. return UA_Server_setVariableNode_valueCallback(server, node, valueCallback);
  132. }
  133. /*************************************************/
  134. /* PubSubConnection */
  135. /*************************************************/
  136. UA_StatusCode
  137. addPubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection){
  138. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  139. if(connection->config->name.length > 512)
  140. return UA_STATUSCODE_BADOUTOFMEMORY;
  141. UA_STACKARRAY(char, connectionName, sizeof(char) * connection->config->name.length +1);
  142. memcpy(connectionName, connection->config->name.data, connection->config->name.length);
  143. connectionName[connection->config->name.length] = '\0';
  144. //This code block must use a lock
  145. UA_Nodestore_remove(server, &connection->identifier);
  146. UA_NodeId pubSubConnectionNodeId;
  147. UA_ObjectAttributes attr = UA_ObjectAttributes_default;
  148. attr.displayName = UA_LOCALIZEDTEXT("de-DE", connectionName);
  149. retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric),
  150. UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPUBSUBCONNECTION),
  151. UA_QUALIFIEDNAME(0, connectionName), UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], NULL, &pubSubConnectionNodeId);
  152. addPubSubObjectNode(server, "Address", connection->identifier.identifier.numeric+1, pubSubConnectionNodeId.identifier.numeric, UA_NS0ID_HASCOMPONENT, UA_NS0ID_NETWORKADDRESSURLTYPE);
  153. UA_Server_addNode_finish(server, pubSubConnectionNodeId);
  154. //End lock zone
  155. UA_NodeId addressNode, urlNode, interfaceNode;
  156. addressNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Address"),
  157. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  158. UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric));
  159. urlNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Url"),
  160. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), addressNode);
  161. interfaceNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "NetworkInterface"),
  162. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), addressNode);
  163. UA_NetworkAddressUrlDataType *networkAddressUrlDataType = ((UA_NetworkAddressUrlDataType *) connection->config->address.data);
  164. UA_Variant value;
  165. UA_Variant_init(&value);
  166. UA_Variant_setScalar(&value, &networkAddressUrlDataType->url, &UA_TYPES[UA_TYPES_STRING]);
  167. UA_Server_writeValue(server, urlNode, value);
  168. UA_Variant_setScalar(&value, &networkAddressUrlDataType->networkInterface, &UA_TYPES[UA_TYPES_STRING]);
  169. UA_Server_writeValue(server, interfaceNode, value);
  170. return retVal;
  171. }
  172. UA_StatusCode
  173. removePubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection){
  174. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  175. retVal |= UA_Server_deleteNode(server, connection->identifier, true);
  176. return retVal;
  177. }
  178. /*************************************************/
  179. /* PublishedDataSet */
  180. /*************************************************/
  181. UA_StatusCode
  182. addPublishedDataItemsRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet){
  183. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  184. if(publishedDataSet->config.name.length > 512)
  185. return UA_STATUSCODE_BADOUTOFMEMORY;
  186. UA_STACKARRAY(char, pdsName, sizeof(char) * publishedDataSet->config.name.length +1);
  187. memcpy(pdsName, publishedDataSet->config.name.data, publishedDataSet->config.name.length);
  188. pdsName[publishedDataSet->config.name.length] = '\0';
  189. //This code block must use a lock
  190. UA_Nodestore_remove(server, &publishedDataSet->identifier);
  191. retVal |= addPubSubObjectNode(server, pdsName, publishedDataSet->identifier.identifier.numeric, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS,
  192. UA_NS0ID_HASPROPERTY, UA_NS0ID_PUBLISHEDDATAITEMSTYPE);
  193. //End lock zone
  194. UA_NodeId configurationVersionNode;
  195. configurationVersionNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConfigurationVersion"),
  196. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  197. UA_NODEID_NUMERIC(0, publishedDataSet->identifier.identifier.numeric));
  198. UA_Variant value;
  199. UA_Variant_init(&value);
  200. UA_Variant_setScalar(&value, &publishedDataSet->dataSetMetaData.configurationVersion, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE]);
  201. UA_Server_writeValue(server, configurationVersionNode, value);
  202. return retVal;
  203. }
  204. UA_StatusCode
  205. removePublishedDataSetRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet){
  206. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  207. retVal |= UA_Server_deleteNode(server, publishedDataSet->identifier, false);
  208. return retVal;
  209. }
  210. /**********************************************/
  211. /* WriterGroup */
  212. /**********************************************/
  213. UA_StatusCode
  214. addWriterGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup){
  215. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  216. if(writerGroup->config.name.length > 512)
  217. return UA_STATUSCODE_BADOUTOFMEMORY;
  218. UA_STACKARRAY(char, wgName, sizeof(char) * writerGroup->config.name.length + 1);
  219. memcpy(wgName, writerGroup->config.name.data, writerGroup->config.name.length);
  220. wgName[writerGroup->config.name.length] = '\0';
  221. //This code block must use a lock
  222. UA_Nodestore_remove(server, &writerGroup->identifier);
  223. retVal |= addPubSubObjectNode(server, wgName, writerGroup->identifier.identifier.numeric, writerGroup->linkedConnection.identifier.numeric,
  224. UA_NS0ID_HASCOMPONENT, UA_NS0ID_WRITERGROUPTYPE);
  225. //End lock zone
  226. UA_NodeId keepAliveNode, publishingIntervalNode, priorityNode, writerGroupIdNode;
  227. keepAliveNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "KeepAliveTime"),
  228. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  229. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  230. publishingIntervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"),
  231. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  232. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  233. UA_NodePropertyContext * publishingIntervalContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext));
  234. publishingIntervalContext->parentNodeId = writerGroup->identifier;
  235. publishingIntervalContext->parentCalssifier = UA_NS0ID_WRITERGROUPTYPE;
  236. publishingIntervalContext->elementClassiefier = UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL;
  237. UA_ValueCallback valueCallback;
  238. valueCallback.onRead = onRead;
  239. valueCallback.onWrite = onWrite;
  240. retVal |= addVariableValueSource(server, valueCallback, publishingIntervalNode, publishingIntervalContext);
  241. UA_Server_writeAccessLevel(server, publishingIntervalNode, (UA_ACCESSLEVELMASK_READ ^ UA_ACCESSLEVELMASK_WRITE));
  242. priorityNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Priority"),
  243. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  244. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  245. writerGroupIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "WriterGroupId"),
  246. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  247. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  248. UA_Variant value;
  249. UA_Variant_init(&value);
  250. UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]);
  251. UA_Server_writeValue(server, publishingIntervalNode, value);
  252. UA_Variant_setScalar(&value, &writerGroup->config.keepAliveTime, &UA_TYPES[UA_TYPES_DURATION]);
  253. UA_Server_writeValue(server, keepAliveNode, value);
  254. UA_Variant_setScalar(&value, &writerGroup->config.priority, &UA_TYPES[UA_TYPES_BYTE]);
  255. UA_Server_writeValue(server, priorityNode, value);
  256. UA_Variant_setScalar(&value, &writerGroup->config.writerGroupId, &UA_TYPES[UA_TYPES_UINT16]);
  257. UA_Server_writeValue(server, writerGroupIdNode, value);
  258. return retVal;
  259. }
  260. UA_StatusCode
  261. removeWriterGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup) {
  262. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  263. retVal |= UA_Server_deleteNode(server, writerGroup->identifier, false);
  264. return retVal;
  265. }
  266. /**********************************************/
  267. /* DataSetWriter */
  268. /**********************************************/
  269. UA_StatusCode
  270. addDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter){
  271. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  272. if(dataSetWriter->config.name.length > 512)
  273. return UA_STATUSCODE_BADOUTOFMEMORY;
  274. UA_STACKARRAY(char, dswName, sizeof(char) * dataSetWriter->config.name.length + 1);
  275. memcpy(dswName, dataSetWriter->config.name.data, dataSetWriter->config.name.length);
  276. dswName[dataSetWriter->config.name.length] = '\0';
  277. //This code block must use a lock
  278. UA_Nodestore_remove(server, &dataSetWriter->identifier);
  279. retVal |= addPubSubObjectNode(server, dswName, dataSetWriter->identifier.identifier.numeric, dataSetWriter->linkedWriterGroup.identifier.numeric,
  280. UA_NS0ID_HASDATASETWRITER, UA_NS0ID_DATASETWRITERTYPE);
  281. //End lock zone
  282. retVal |= UA_Server_addReference(server, dataSetWriter->connectedDataSet,
  283. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETTOWRITER),
  284. UA_EXPANDEDNODEID_NUMERIC(0, dataSetWriter->identifier.identifier.numeric), true);
  285. retVal |= UA_Server_addReference(server, dataSetWriter->connectedDataSet,
  286. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETTOWRITER),
  287. UA_EXPANDEDNODEID_NUMERIC(0, dataSetWriter->identifier.identifier.numeric), false);
  288. return retVal;
  289. }
  290. UA_StatusCode
  291. removeDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter) {
  292. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  293. retVal |= UA_Server_deleteNode(server, dataSetWriter->identifier, false);
  294. return retVal;
  295. }
  296. /**********************************************/
  297. /* Destructors */
  298. /**********************************************/
  299. static void
  300. connectionTypeDestructor(UA_Server *server,
  301. const UA_NodeId *sessionId, void *sessionContext,
  302. const UA_NodeId *typeId, void *typeContext,
  303. const UA_NodeId *nodeId, void **nodeContext) {
  304. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_USERLAND, "Connection destructor called!");
  305. }
  306. static void
  307. writerGroupTypeDestructor(UA_Server *server,
  308. const UA_NodeId *sessionId, void *sessionContext,
  309. const UA_NodeId *typeId, void *typeContext,
  310. const UA_NodeId *nodeId, void **nodeContext) {
  311. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_USERLAND, "WriterGroup destructor called!");
  312. UA_NodeId intervalNode;
  313. intervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"),
  314. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId);
  315. UA_NodePropertyContext *internalConnectionContext;
  316. UA_Server_getNodeContext(server, intervalNode, (void **) &internalConnectionContext);
  317. if(!UA_NodeId_equal(&UA_NODEID_NULL , &intervalNode)){
  318. UA_free(internalConnectionContext);
  319. }
  320. }
  321. static void
  322. dataSetWriterTypeDestructor(UA_Server *server,
  323. const UA_NodeId *sessionId, void *sessionContext,
  324. const UA_NodeId *typeId, void *typeContext,
  325. const UA_NodeId *nodeId, void **nodeContext) {
  326. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_USERLAND, "DataSetWriter destructor called!");
  327. }
  328. UA_StatusCode
  329. UA_Server_initPubSubNS0(UA_Server *server) {
  330. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  331. UA_String profileArray[1];
  332. profileArray[0] = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
  333. retVal |= writePubSubNs0VariableArray(server, UA_NS0ID_PUBLISHSUBSCRIBE_SUPPORTEDTRANSPORTPROFILES,
  334. profileArray,
  335. 1, &UA_TYPES[UA_TYPES_STRING]);
  336. UA_NodeTypeLifecycle liveCycle;
  337. liveCycle.constructor = NULL;
  338. liveCycle.destructor = connectionTypeDestructor;
  339. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE), liveCycle);
  340. liveCycle.destructor = writerGroupTypeDestructor;
  341. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE), liveCycle);
  342. liveCycle.destructor = dataSetWriterTypeDestructor;
  343. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETWRITERDATATYPE), liveCycle);
  344. return retVal;
  345. }
  346. #endif /* UA_ENABLE_PUBSUB_INFORMATIONMODEL */