ua_pubsub_ns0.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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. switch(((UA_NodePropertyContext *) nodeContext)->parentCalssifier){
  73. case UA_NS0ID_PUBSUBCONNECTIONTYPE:
  74. break;
  75. case UA_NS0ID_WRITERGROUPTYPE:
  76. myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId;
  77. UA_WriterGroup *writerGroup = UA_WriterGroup_findWGbyId(server, myNodeId);
  78. if(!writerGroup)
  79. return;
  80. switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier){
  81. case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL:
  82. UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]);
  83. break;
  84. default:
  85. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown property.");
  86. }
  87. break;
  88. default:
  89. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown parent element.");
  90. }
  91. UA_Server_writeValue(server, *nodeid, value);
  92. }
  93. static void
  94. onWrite(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
  95. const UA_NodeId *nodeId, void *nodeContext,
  96. const UA_NumericRange *range, const UA_DataValue *data){
  97. UA_Variant value;
  98. UA_NodeId myNodeId;
  99. switch(((UA_NodePropertyContext *) nodeContext)->parentCalssifier){
  100. case UA_NS0ID_PUBSUBCONNECTIONTYPE:
  101. break;
  102. case UA_NS0ID_WRITERGROUPTYPE:
  103. myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId;
  104. UA_WriterGroup *writerGroup = UA_WriterGroup_findWGbyId(server, myNodeId);
  105. UA_WriterGroupConfig writerGroupConfig;
  106. memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
  107. if(!writerGroup)
  108. return;
  109. switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier){
  110. case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL:
  111. UA_Server_getWriterGroupConfig(server, writerGroup->identifier, &writerGroupConfig);
  112. writerGroupConfig.publishingInterval = *((UA_Duration *) data->value.data);
  113. UA_Server_updateWriterGroupConfig(server, writerGroup->identifier, &writerGroupConfig);
  114. UA_Variant_setScalar(&value, data->value.data, &UA_TYPES[UA_TYPES_DURATION]);
  115. UA_WriterGroupConfig_deleteMembers(&writerGroupConfig);
  116. break;
  117. default:
  118. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Write error! Unknown property element.");
  119. }
  120. break;
  121. default:
  122. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown parent element.");
  123. }
  124. }
  125. static UA_StatusCode
  126. addVariableValueSource(UA_Server *server, UA_ValueCallback valueCallback,
  127. UA_NodeId node, UA_NodePropertyContext *context){
  128. UA_Server_setNodeContext(server, node, context);
  129. return UA_Server_setVariableNode_valueCallback(server, node, valueCallback);
  130. }
  131. /*************************************************/
  132. /* PubSubConnection */
  133. /*************************************************/
  134. UA_StatusCode
  135. addPubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection){
  136. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  137. if(connection->config->name.length > 512)
  138. return UA_STATUSCODE_BADOUTOFMEMORY;
  139. UA_STACKARRAY(char, connectionName, sizeof(char) * connection->config->name.length +1);
  140. memcpy(connectionName, connection->config->name.data, connection->config->name.length);
  141. connectionName[connection->config->name.length] = '\0';
  142. //This code block must use a lock
  143. UA_Nodestore_remove(server, &connection->identifier);
  144. retVal |= addPubSubObjectNode(server, connectionName, connection->identifier.identifier.numeric, UA_NS0ID_PUBLISHSUBSCRIBE,
  145. UA_NS0ID_HASPUBSUBCONNECTION, UA_NS0ID_PUBSUBCONNECTIONTYPE);
  146. //End lock zone
  147. UA_NodeId addressNode, urlNode, interfaceNode;
  148. addressNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Address"),
  149. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  150. UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric));
  151. urlNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Url"),
  152. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), addressNode);
  153. interfaceNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "NetworkInterface"),
  154. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), addressNode);
  155. UA_NetworkAddressUrlDataType *networkAddressUrlDataType = ((UA_NetworkAddressUrlDataType *) connection->config->address.data);
  156. UA_Variant value;
  157. UA_Variant_init(&value);
  158. UA_Variant_setScalar(&value, &networkAddressUrlDataType->url, &UA_TYPES[UA_TYPES_STRING]);
  159. UA_Server_writeValue(server, urlNode, value);
  160. UA_Variant_setScalar(&value, &networkAddressUrlDataType->networkInterface, &UA_TYPES[UA_TYPES_STRING]);
  161. UA_Server_writeValue(server, interfaceNode, value);
  162. return retVal;
  163. }
  164. UA_StatusCode
  165. removePubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection){
  166. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  167. retVal |= UA_Server_deleteNode(server, connection->identifier, true);
  168. return retVal;
  169. }
  170. /*************************************************/
  171. /* PublishedDataSet */
  172. /*************************************************/
  173. UA_StatusCode
  174. addPublishedDataItemsRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet){
  175. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  176. if(publishedDataSet->config.name.length > 512)
  177. return UA_STATUSCODE_BADOUTOFMEMORY;
  178. UA_STACKARRAY(char, pdsName, sizeof(char) * publishedDataSet->config.name.length +1);
  179. memcpy(pdsName, publishedDataSet->config.name.data, publishedDataSet->config.name.length);
  180. pdsName[publishedDataSet->config.name.length] = '\0';
  181. //This code block must use a lock
  182. UA_Nodestore_remove(server, &publishedDataSet->identifier);
  183. retVal |= addPubSubObjectNode(server, pdsName, publishedDataSet->identifier.identifier.numeric, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS,
  184. UA_NS0ID_HASPROPERTY, UA_NS0ID_PUBLISHEDDATAITEMSTYPE);
  185. //End lock zone
  186. UA_NodeId configurationVersionNode;
  187. configurationVersionNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConfigurationVersion"),
  188. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  189. UA_NODEID_NUMERIC(0, publishedDataSet->identifier.identifier.numeric));
  190. UA_Variant value;
  191. UA_Variant_init(&value);
  192. UA_Variant_setScalar(&value, &publishedDataSet->dataSetMetaData.configurationVersion, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE]);
  193. UA_Server_writeValue(server, configurationVersionNode, value);
  194. return retVal;
  195. }
  196. UA_StatusCode
  197. removePublishedDataSetRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet){
  198. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  199. retVal |= UA_Server_deleteNode(server, publishedDataSet->identifier, false);
  200. return retVal;
  201. }
  202. /**********************************************/
  203. /* WriterGroup */
  204. /**********************************************/
  205. UA_StatusCode
  206. addWriterGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup){
  207. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  208. if(writerGroup->config.name.length > 512)
  209. return UA_STATUSCODE_BADOUTOFMEMORY;
  210. UA_STACKARRAY(char, wgName, sizeof(char) * writerGroup->config.name.length + 1);
  211. memcpy(wgName, writerGroup->config.name.data, writerGroup->config.name.length);
  212. wgName[writerGroup->config.name.length] = '\0';
  213. //This code block must use a lock
  214. UA_Nodestore_remove(server, &writerGroup->identifier);
  215. retVal |= addPubSubObjectNode(server, wgName, writerGroup->identifier.identifier.numeric, writerGroup->linkedConnection.identifier.numeric,
  216. UA_NS0ID_HASCOMPONENT, UA_NS0ID_WRITERGROUPTYPE);
  217. //End lock zone
  218. UA_NodeId keepAliveNode, publishingIntervalNode, priorityNode, writerGroupIdNode;
  219. keepAliveNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "KeepAliveTime"),
  220. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  221. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  222. publishingIntervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"),
  223. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  224. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  225. UA_NodePropertyContext * publishingIntervalContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext));
  226. publishingIntervalContext->parentNodeId = writerGroup->identifier;
  227. publishingIntervalContext->parentCalssifier = UA_NS0ID_WRITERGROUPTYPE;
  228. publishingIntervalContext->elementClassiefier = UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL;
  229. retVal |= addVariableValueSource(server,(UA_ValueCallback) {onRead, onWrite}, publishingIntervalNode, publishingIntervalContext);
  230. UA_Server_writeAccessLevel(server, publishingIntervalNode, (UA_ACCESSLEVELMASK_READ ^ UA_ACCESSLEVELMASK_WRITE));
  231. priorityNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Priority"),
  232. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  233. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  234. writerGroupIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "WriterGroupId"),
  235. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  236. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  237. UA_Variant value;
  238. UA_Variant_init(&value);
  239. UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]);
  240. UA_Server_writeValue(server, publishingIntervalNode, value);
  241. UA_Variant_setScalar(&value, &writerGroup->config.keepAliveTime, &UA_TYPES[UA_TYPES_DURATION]);
  242. UA_Server_writeValue(server, keepAliveNode, value);
  243. UA_Variant_setScalar(&value, &writerGroup->config.priority, &UA_TYPES[UA_TYPES_BYTE]);
  244. UA_Server_writeValue(server, priorityNode, value);
  245. UA_Variant_setScalar(&value, &writerGroup->config.writerGroupId, &UA_TYPES[UA_TYPES_UINT16]);
  246. UA_Server_writeValue(server, writerGroupIdNode, value);
  247. return retVal;
  248. }
  249. UA_StatusCode
  250. removeWriterGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup) {
  251. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  252. retVal |= UA_Server_deleteNode(server, writerGroup->identifier, false);
  253. return retVal;
  254. }
  255. /**********************************************/
  256. /* DataSetWriter */
  257. /**********************************************/
  258. UA_StatusCode
  259. addDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter){
  260. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  261. if(dataSetWriter->config.name.length > 512)
  262. return UA_STATUSCODE_BADOUTOFMEMORY;
  263. UA_STACKARRAY(char, dswName, sizeof(char) * dataSetWriter->config.name.length + 1);
  264. memcpy(dswName, dataSetWriter->config.name.data, dataSetWriter->config.name.length);
  265. dswName[dataSetWriter->config.name.length] = '\0';
  266. //This code block must use a lock
  267. UA_Nodestore_remove(server, &dataSetWriter->identifier);
  268. retVal |= addPubSubObjectNode(server, dswName, dataSetWriter->identifier.identifier.numeric, dataSetWriter->linkedWriterGroup.identifier.numeric,
  269. UA_NS0ID_HASDATASETWRITER, UA_NS0ID_DATASETWRITERTYPE);
  270. //End lock zone
  271. retVal |= UA_Server_addReference(server, dataSetWriter->connectedDataSet,
  272. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETTOWRITER),
  273. UA_EXPANDEDNODEID_NUMERIC(0, dataSetWriter->identifier.identifier.numeric), true);
  274. retVal |= UA_Server_addReference(server, dataSetWriter->connectedDataSet,
  275. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETTOWRITER),
  276. UA_EXPANDEDNODEID_NUMERIC(0, dataSetWriter->identifier.identifier.numeric), false);
  277. return retVal;
  278. }
  279. UA_StatusCode
  280. removeDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter) {
  281. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  282. retVal |= UA_Server_deleteNode(server, dataSetWriter->identifier, false);
  283. return retVal;
  284. }
  285. /**********************************************/
  286. /* Destructors */
  287. /**********************************************/
  288. static void
  289. connectionTypeDestructor(UA_Server *server,
  290. const UA_NodeId *sessionId, void *sessionContext,
  291. const UA_NodeId *typeId, void *typeContext,
  292. const UA_NodeId *nodeId, void **nodeContext) {
  293. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_USERLAND, "Connection destructor called!");
  294. }
  295. static void
  296. writerGroupTypeDestructor(UA_Server *server,
  297. const UA_NodeId *sessionId, void *sessionContext,
  298. const UA_NodeId *typeId, void *typeContext,
  299. const UA_NodeId *nodeId, void **nodeContext) {
  300. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_USERLAND, "WriterGroup destructor called!");
  301. UA_NodeId intervalNode;
  302. intervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"),
  303. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId);
  304. UA_NodePropertyContext *internalConnectionContext;
  305. UA_Server_getNodeContext(server, intervalNode, (void **) &internalConnectionContext);
  306. if(!UA_NodeId_equal(&UA_NODEID_NULL , &intervalNode)){
  307. UA_free(internalConnectionContext);
  308. }
  309. }
  310. static void
  311. dataSetWriterTypeDestructor(UA_Server *server,
  312. const UA_NodeId *sessionId, void *sessionContext,
  313. const UA_NodeId *typeId, void *typeContext,
  314. const UA_NodeId *nodeId, void **nodeContext) {
  315. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_USERLAND, "DataSetWriter destructor called!");
  316. }
  317. UA_StatusCode
  318. UA_Server_initPubSubNS0(UA_Server *server) {
  319. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  320. UA_String profileArray[1];
  321. profileArray[0] = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
  322. retVal |= writePubSubNs0VariableArray(server, UA_NS0ID_PUBLISHSUBSCRIBE_SUPPORTEDTRANSPORTPROFILES,
  323. profileArray,
  324. 1, &UA_TYPES[UA_TYPES_STRING]);
  325. UA_NodeTypeLifecycle liveCycle;
  326. liveCycle.constructor = NULL;
  327. liveCycle.destructor = connectionTypeDestructor;
  328. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE), liveCycle);
  329. liveCycle.destructor = writerGroupTypeDestructor;
  330. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE), liveCycle);
  331. liveCycle.destructor = dataSetWriterTypeDestructor;
  332. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETWRITERDATATYPE), liveCycle);
  333. return retVal;
  334. }
  335. #endif /* UA_ENABLE_PUBSUB_INFORMATIONMODEL */