ua_pubsub_ns0.c 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105
  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. * Copyright (c) 2019 Kalycito Infotech Private Limited
  7. */
  8. #include "ua_pubsub_ns0.h"
  9. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* conditional compilation */
  10. typedef struct{
  11. UA_NodeId parentNodeId;
  12. UA_UInt32 parentClassifier;
  13. UA_UInt32 elementClassiefier;
  14. } UA_NodePropertyContext;
  15. //Prototypes
  16. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  17. static UA_StatusCode addWriterGroupAction(UA_Server *server,
  18. const UA_NodeId *sessionId, void *sessionHandle,
  19. const UA_NodeId *methodId, void *methodContext,
  20. const UA_NodeId *objectId, void *objectContext,
  21. size_t inputSize, const UA_Variant *input,
  22. size_t outputSize, UA_Variant *output);
  23. static UA_StatusCode removeGroupAction(UA_Server *server,
  24. const UA_NodeId *sessionId, void *sessionHandle,
  25. const UA_NodeId *methodId, void *methodContext,
  26. const UA_NodeId *objectId, void *objectContext,
  27. size_t inputSize, const UA_Variant *input,
  28. size_t outputSize, UA_Variant *output);
  29. static UA_StatusCode addDataSetWriterAction(UA_Server *server,
  30. const UA_NodeId *sessionId, void *sessionHandle,
  31. const UA_NodeId *methodId, void *methodContext,
  32. const UA_NodeId *objectId, void *objectContext,
  33. size_t inputSize, const UA_Variant *input,
  34. size_t outputSize, UA_Variant *output);
  35. #endif
  36. static UA_StatusCode
  37. addPubSubObjectNode(UA_Server *server, char* name, UA_UInt32 objectid,
  38. UA_UInt32 parentid, UA_UInt32 referenceid, UA_UInt32 type_id) {
  39. UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
  40. object_attr.displayName = UA_LOCALIZEDTEXT("", name);
  41. return UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(0, objectid),
  42. UA_NODEID_NUMERIC(0, parentid),
  43. UA_NODEID_NUMERIC(0, referenceid),
  44. UA_QUALIFIEDNAME(0, name),
  45. UA_NODEID_NUMERIC(0, type_id),
  46. object_attr, NULL, NULL);
  47. }
  48. static UA_StatusCode
  49. writePubSubNs0VariableArray(UA_Server *server, UA_UInt32 id, void *v,
  50. size_t length, const UA_DataType *type) {
  51. UA_Variant var;
  52. UA_Variant_init(&var);
  53. UA_Variant_setArray(&var, v, length, type);
  54. return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var);
  55. }
  56. static UA_NodeId
  57. findSingleChildNode(UA_Server *server, UA_QualifiedName targetName,
  58. UA_NodeId referenceTypeId, UA_NodeId startingNode){
  59. UA_NodeId resultNodeId;
  60. UA_RelativePathElement rpe;
  61. UA_RelativePathElement_init(&rpe);
  62. rpe.referenceTypeId = referenceTypeId;
  63. rpe.isInverse = false;
  64. rpe.includeSubtypes = false;
  65. rpe.targetName = targetName;
  66. UA_BrowsePath bp;
  67. UA_BrowsePath_init(&bp);
  68. bp.startingNode = startingNode;
  69. bp.relativePath.elementsSize = 1;
  70. bp.relativePath.elements = &rpe;
  71. UA_BrowsePathResult bpr =
  72. UA_Server_translateBrowsePathToNodeIds(server, &bp);
  73. if(bpr.statusCode != UA_STATUSCODE_GOOD ||
  74. bpr.targetsSize < 1)
  75. return UA_NODEID_NULL;
  76. if(UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, &resultNodeId) != UA_STATUSCODE_GOOD){
  77. UA_BrowsePathResult_deleteMembers(&bpr);
  78. return UA_NODEID_NULL;
  79. }
  80. UA_BrowsePathResult_deleteMembers(&bpr);
  81. return resultNodeId;
  82. }
  83. static void
  84. onRead(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
  85. const UA_NodeId *nodeid, void *nodeContext,
  86. const UA_NumericRange *range, const UA_DataValue *data) {
  87. UA_Variant value;
  88. UA_Variant_init(&value);
  89. UA_NodeId myNodeId;
  90. UA_WriterGroup *writerGroup = NULL;
  91. UA_PubSubConnection *pubSubConnection = NULL;
  92. UA_PublishedDataSet *publishedDataSet = NULL;
  93. UA_PublishedVariableDataType *pvd = NULL;
  94. size_t counter = 0;
  95. switch(((UA_NodePropertyContext *) nodeContext)->parentClassifier){
  96. case UA_NS0ID_PUBSUBCONNECTIONTYPE:
  97. myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId;
  98. pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, myNodeId);
  99. switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier) {
  100. case UA_NS0ID_PUBSUBCONNECTIONTYPE_PUBLISHERID:
  101. if(pubSubConnection->config->publisherIdType == UA_PUBSUB_PUBLISHERID_STRING) {
  102. UA_Variant_setScalar(&value, &pubSubConnection->config->publisherId.numeric,
  103. &UA_TYPES[UA_TYPES_STRING]);
  104. }else
  105. UA_Variant_setScalar(&value, &pubSubConnection->config->publisherId.numeric, &UA_TYPES[UA_TYPES_UINT32]);
  106. break;
  107. default:
  108. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  109. "Read error! Unknown property.");
  110. }
  111. break;
  112. case UA_NS0ID_WRITERGROUPTYPE:
  113. myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId;
  114. writerGroup = UA_WriterGroup_findWGbyId(server, myNodeId);
  115. if(!writerGroup)
  116. return;
  117. switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier){
  118. case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL:
  119. UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]);
  120. break;
  121. default:
  122. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  123. "Read error! Unknown property.");
  124. }
  125. break;
  126. case UA_NS0ID_PUBLISHEDDATAITEMSTYPE:
  127. myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId;
  128. publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, myNodeId);
  129. if(!publishedDataSet)
  130. return;
  131. switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier) {
  132. case UA_NS0ID_PUBLISHEDDATAITEMSTYPE_PUBLISHEDDATA:
  133. pvd = (UA_PublishedVariableDataType *)
  134. UA_calloc(publishedDataSet->fieldSize, sizeof(UA_PublishedVariableDataType));
  135. UA_DataSetField *field;
  136. LIST_FOREACH(field, &publishedDataSet->fields, listEntry) {
  137. pvd[counter].attributeId = UA_ATTRIBUTEID_VALUE;
  138. pvd[counter].publishedVariable = field->config.field.variable.publishParameters.publishedVariable;
  139. //UA_NodeId_copy(&field->config.field.variable.publishParameters.publishedVariable, &pvd[counter].publishedVariable);
  140. counter++;
  141. }
  142. UA_Variant_setArray(&value, pvd, publishedDataSet->fieldSize,
  143. &UA_TYPES[UA_TYPES_PUBLISHEDVARIABLEDATATYPE]);
  144. break;
  145. default:
  146. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  147. "Read error! Unknown property.");
  148. }
  149. break;
  150. default:
  151. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  152. "Read error! Unknown parent element.");
  153. }
  154. UA_Server_writeValue(server, *nodeid, value);
  155. }
  156. static void
  157. onWrite(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
  158. const UA_NodeId *nodeId, void *nodeContext,
  159. const UA_NumericRange *range, const UA_DataValue *data){
  160. UA_Variant value;
  161. UA_NodeId myNodeId;
  162. UA_WriterGroup *writerGroup = NULL;
  163. switch(((UA_NodePropertyContext *) nodeContext)->parentClassifier){
  164. case UA_NS0ID_PUBSUBCONNECTIONTYPE:
  165. //no runtime writable attributes
  166. break;
  167. case UA_NS0ID_WRITERGROUPTYPE:
  168. myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId;
  169. writerGroup = UA_WriterGroup_findWGbyId(server, myNodeId);
  170. UA_WriterGroupConfig writerGroupConfig;
  171. memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
  172. if(!writerGroup)
  173. return;
  174. switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier){
  175. case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL:
  176. UA_Server_getWriterGroupConfig(server, writerGroup->identifier, &writerGroupConfig);
  177. writerGroupConfig.publishingInterval = *((UA_Duration *) data->value.data);
  178. UA_Server_updateWriterGroupConfig(server, writerGroup->identifier, &writerGroupConfig);
  179. UA_Variant_setScalar(&value, data->value.data, &UA_TYPES[UA_TYPES_DURATION]);
  180. UA_WriterGroupConfig_deleteMembers(&writerGroupConfig);
  181. break;
  182. default:
  183. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  184. "Write error! Unknown property element.");
  185. }
  186. break;
  187. default:
  188. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  189. "Read error! Unknown parent element.");
  190. }
  191. }
  192. static UA_StatusCode
  193. addVariableValueSource(UA_Server *server, UA_ValueCallback valueCallback,
  194. UA_NodeId node, UA_NodePropertyContext *context){
  195. UA_Server_setNodeContext(server, node, context);
  196. return UA_Server_setVariableNode_valueCallback(server, node, valueCallback);
  197. }
  198. /*************************************************/
  199. /* PubSubConnection */
  200. /*************************************************/
  201. UA_StatusCode
  202. addPubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection){
  203. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  204. if(connection->config->name.length > 512)
  205. return UA_STATUSCODE_BADOUTOFMEMORY;
  206. UA_STACKARRAY(char, connectionName, sizeof(char) * connection->config->name.length +1);
  207. memcpy(connectionName, connection->config->name.data, connection->config->name.length);
  208. connectionName[connection->config->name.length] = '\0';
  209. //This code block must use a lock
  210. UA_Nodestore_removeNode(server->nsCtx, &connection->identifier);
  211. UA_NodeId pubSubConnectionNodeId;
  212. UA_ObjectAttributes attr = UA_ObjectAttributes_default;
  213. attr.displayName = UA_LOCALIZEDTEXT("de-DE", connectionName);
  214. retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT,
  215. UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric),
  216. UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE),
  217. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPUBSUBCONNECTION),
  218. UA_QUALIFIEDNAME(0, connectionName),
  219. UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE),
  220. (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
  221. NULL, &pubSubConnectionNodeId);
  222. addPubSubObjectNode(server, "Address", connection->identifier.identifier.numeric+1,
  223. pubSubConnectionNodeId.identifier.numeric, UA_NS0ID_HASCOMPONENT,
  224. UA_NS0ID_NETWORKADDRESSURLTYPE);
  225. UA_Server_addNode_finish(server, pubSubConnectionNodeId);
  226. //End lock zone
  227. UA_NodeId addressNode, urlNode, interfaceNode, publisherIdNode, connectionPropertieNode, transportProfileUri;
  228. addressNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Address"),
  229. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  230. UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric));
  231. urlNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Url"),
  232. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), addressNode);
  233. interfaceNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "NetworkInterface"),
  234. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), addressNode);
  235. publisherIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"),
  236. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  237. UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric));
  238. connectionPropertieNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConnectionProperties"),
  239. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  240. UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric));
  241. transportProfileUri = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "TransportProfileUri"),
  242. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  243. UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric));
  244. if (UA_NodeId_equal(&addressNode, &UA_NODEID_NULL) ||
  245. UA_NodeId_equal(&urlNode, &UA_NODEID_NULL) ||
  246. UA_NodeId_equal(&interfaceNode, &UA_NODEID_NULL) ||
  247. UA_NodeId_equal(&publisherIdNode, &UA_NODEID_NULL) ||
  248. UA_NodeId_equal(&connectionPropertieNode, &UA_NODEID_NULL) ||
  249. UA_NodeId_equal(&transportProfileUri, &UA_NODEID_NULL)) {
  250. return UA_STATUSCODE_BADNOTFOUND;
  251. }
  252. retVal |= writePubSubNs0VariableArray(server, connectionPropertieNode.identifier.numeric,
  253. connection->config->connectionProperties,
  254. connection->config->connectionPropertiesSize,
  255. &UA_TYPES[UA_TYPES_KEYVALUEPAIR]);
  256. UA_NetworkAddressUrlDataType *networkAddressUrlDataType = ((UA_NetworkAddressUrlDataType *) connection->config->address.data);
  257. UA_Variant value;
  258. UA_Variant_init(&value);
  259. UA_Variant_setScalar(&value, &networkAddressUrlDataType->url, &UA_TYPES[UA_TYPES_STRING]);
  260. UA_Server_writeValue(server, urlNode, value);
  261. UA_Variant_setScalar(&value, &networkAddressUrlDataType->networkInterface, &UA_TYPES[UA_TYPES_STRING]);
  262. UA_Server_writeValue(server, interfaceNode, value);
  263. UA_Variant_setScalar(&value, &connection->config->transportProfileUri, &UA_TYPES[UA_TYPES_STRING]);
  264. UA_Server_writeValue(server, transportProfileUri, value);
  265. UA_NodePropertyContext *connectionPublisherIdContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext));
  266. connectionPublisherIdContext->parentNodeId = connection->identifier;
  267. connectionPublisherIdContext->parentClassifier = UA_NS0ID_PUBSUBCONNECTIONTYPE;
  268. connectionPublisherIdContext->elementClassiefier = UA_NS0ID_PUBSUBCONNECTIONTYPE_PUBLISHERID;
  269. UA_ValueCallback valueCallback;
  270. valueCallback.onRead = onRead;
  271. valueCallback.onWrite = NULL;
  272. retVal |= addVariableValueSource(server, valueCallback, publisherIdNode, connectionPublisherIdContext);
  273. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  274. retVal |= UA_Server_addReference(server, connection->identifier,
  275. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  276. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDWRITERGROUP), true);
  277. retVal |= UA_Server_addReference(server, connection->identifier,
  278. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  279. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDREADERGROUP), true);
  280. retVal |= UA_Server_addReference(server, connection->identifier,
  281. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  282. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_REMOVEGROUP), true);
  283. #endif
  284. return retVal;
  285. }
  286. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  287. static UA_StatusCode
  288. addPubSubConnectionAction(UA_Server *server,
  289. const UA_NodeId *sessionId, void *sessionHandle,
  290. const UA_NodeId *methodId, void *methodContext,
  291. const UA_NodeId *objectId, void *objectContext,
  292. size_t inputSize, const UA_Variant *input,
  293. size_t outputSize, UA_Variant *output){
  294. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  295. UA_PubSubConnectionDataType pubSubConnectionDataType = *((UA_PubSubConnectionDataType *) input[0].data);
  296. UA_NetworkAddressUrlDataType networkAddressUrlDataType;
  297. memset(&networkAddressUrlDataType, 0, sizeof(networkAddressUrlDataType));
  298. UA_ExtensionObject eo = pubSubConnectionDataType.address;
  299. if(eo.encoding == UA_EXTENSIONOBJECT_DECODED){
  300. if(eo.content.decoded.type == &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]){
  301. if(UA_NetworkAddressUrlDataType_copy((UA_NetworkAddressUrlDataType *) eo.content.decoded.data,
  302. &networkAddressUrlDataType) != UA_STATUSCODE_GOOD){
  303. return UA_STATUSCODE_BADOUTOFMEMORY;
  304. }
  305. }
  306. }
  307. UA_PubSubConnectionConfig connectionConfig;
  308. memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
  309. connectionConfig.transportProfileUri = pubSubConnectionDataType.transportProfileUri;
  310. connectionConfig.name = pubSubConnectionDataType.name;
  311. //TODO set real connection state
  312. connectionConfig.enabled = pubSubConnectionDataType.enabled;
  313. //connectionConfig.enabled = pubSubConnectionDataType.enabled;
  314. UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrlDataType,
  315. &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
  316. if(pubSubConnectionDataType.publisherId.type == &UA_TYPES[UA_TYPES_UINT32]){
  317. connectionConfig.publisherId.numeric = * ((UA_UInt32 *) pubSubConnectionDataType.publisherId.data);
  318. } else if (pubSubConnectionDataType.publisherId.type == &UA_TYPES[UA_TYPES_STRING]){
  319. connectionConfig.publisherIdType = UA_PUBSUB_PUBLISHERID_STRING;
  320. UA_String_copy((UA_String *) pubSubConnectionDataType.publisherId.data, &connectionConfig.publisherId.string);
  321. } else {
  322. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Unsupported PublisherId Type used.");
  323. //TODO what's the best default behaviour here?
  324. connectionConfig.publisherId.numeric = 0;
  325. }
  326. //call API function and create the connection
  327. UA_NodeId connectionId;
  328. retVal |= UA_Server_addPubSubConnection(server, &connectionConfig, &connectionId);
  329. if(retVal != UA_STATUSCODE_GOOD){
  330. return retVal;
  331. }
  332. for(size_t i = 0; i < pubSubConnectionDataType.writerGroupsSize; i++){
  333. //UA_PubSubConnection_addWriterGroup(server, UA_NODEID_NULL, NULL, NULL);
  334. }
  335. for(size_t i = 0; i < pubSubConnectionDataType.readerGroupsSize; i++){
  336. //UA_Server_addReaderGroup(server, NULL, NULL, NULL);
  337. }
  338. UA_NetworkAddressUrlDataType_deleteMembers(&networkAddressUrlDataType);
  339. //set ouput value
  340. UA_Variant_setScalarCopy(output, &connectionId, &UA_TYPES[UA_TYPES_NODEID]);
  341. return UA_STATUSCODE_GOOD;
  342. }
  343. #endif
  344. UA_StatusCode
  345. removePubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection){
  346. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  347. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  348. retVal |= UA_Server_deleteReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  349. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDWRITERGROUP),
  350. false);
  351. retVal |= UA_Server_deleteReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  352. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDREADERGROUP),
  353. false);
  354. retVal |= UA_Server_deleteReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  355. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_REMOVEGROUP),
  356. false);
  357. #endif
  358. retVal |= UA_Server_deleteNode(server, connection->identifier, true);
  359. return retVal;
  360. }
  361. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  362. static UA_StatusCode
  363. removeConnectionAction(UA_Server *server,
  364. const UA_NodeId *sessionId, void *sessionHandle,
  365. const UA_NodeId *methodId, void *methodContext,
  366. const UA_NodeId *objectId, void *objectContext,
  367. size_t inputSize, const UA_Variant *input,
  368. size_t outputSize, UA_Variant *output){
  369. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  370. UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
  371. retVal |= UA_Server_removePubSubConnection(server, nodeToRemove);
  372. if(retVal == UA_STATUSCODE_BADNOTFOUND)
  373. retVal = UA_STATUSCODE_BADNODEIDUNKNOWN;
  374. return retVal;
  375. }
  376. #endif
  377. /**********************************************/
  378. /* DataSetReader */
  379. /**********************************************/
  380. UA_StatusCode
  381. addDataSetReaderRepresentation(UA_Server *server, UA_DataSetReader *dataSetReader){
  382. //TODO implement reader part
  383. return UA_STATUSCODE_BADNOTIMPLEMENTED;
  384. }
  385. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  386. static UA_StatusCode
  387. addDataSetReaderAction(UA_Server *server,
  388. const UA_NodeId *sessionId, void *sessionHandle,
  389. const UA_NodeId *methodId, void *methodContext,
  390. const UA_NodeId *objectId, void *objectContext,
  391. size_t inputSize, const UA_Variant *input,
  392. size_t outputSize, UA_Variant *output){
  393. UA_StatusCode retVal = UA_STATUSCODE_BADNOTIMPLEMENTED;
  394. //TODO implement reader part
  395. return retVal;
  396. }
  397. #endif
  398. UA_StatusCode
  399. removeDataSetReaderRepresentation(UA_Server *server, UA_DataSetReader* dataSetReader){
  400. //TODO implement reader part
  401. return UA_STATUSCODE_BADNOTIMPLEMENTED;
  402. }
  403. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  404. static UA_StatusCode
  405. removeDataSetReaderAction(UA_Server *server,
  406. const UA_NodeId *sessionId, void *sessionHandle,
  407. const UA_NodeId *methodId, void *methodContext,
  408. const UA_NodeId *objectId, void *objectContext,
  409. size_t inputSize, const UA_Variant *input,
  410. size_t outputSize, UA_Variant *output){
  411. UA_StatusCode retVal = UA_STATUSCODE_BADNOTIMPLEMENTED;
  412. //TODO implement reader part
  413. return retVal;
  414. }
  415. #endif
  416. /*************************************************/
  417. /* PublishedDataSet */
  418. /*************************************************/
  419. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  420. static UA_StatusCode
  421. addDataSetFolderAction(UA_Server *server,
  422. const UA_NodeId *sessionId, void *sessionHandle,
  423. const UA_NodeId *methodId, void *methodContext,
  424. const UA_NodeId *objectId, void *objectContext,
  425. size_t inputSize, const UA_Variant *input,
  426. size_t outputSize, UA_Variant *output){
  427. /* defined in R 1.04 9.1.4.5.7 */
  428. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  429. UA_String newFolderName = *((UA_String *) input[0].data);
  430. UA_NodeId generatedId;
  431. UA_ObjectAttributes objectAttributes = UA_ObjectAttributes_default;
  432. UA_LocalizedText name = {UA_STRING("en-US"), newFolderName};
  433. objectAttributes.displayName = name;
  434. retVal |= UA_Server_addObjectNode(server, UA_NODEID_NULL, *objectId, UA_NODEID_NUMERIC(0,UA_NS0ID_ORGANIZES),
  435. UA_QUALIFIEDNAME(0, "DataSetFolder"), UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE),
  436. objectAttributes, NULL, &generatedId);
  437. UA_Variant_setScalarCopy(output, &generatedId, &UA_TYPES[UA_TYPES_NODEID]);
  438. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  439. retVal |= UA_Server_addReference(server, generatedId,
  440. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  441. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), true);
  442. retVal |= UA_Server_addReference(server, generatedId,
  443. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  444. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), true);
  445. retVal |= UA_Server_addReference(server, generatedId,
  446. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  447. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER), true);
  448. retVal |= UA_Server_addReference(server, generatedId,
  449. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  450. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER), true);
  451. #endif
  452. return retVal;
  453. }
  454. #endif
  455. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  456. static UA_StatusCode
  457. removeDataSetFolderAction(UA_Server *server,
  458. const UA_NodeId *sessionId, void *sessionHandle,
  459. const UA_NodeId *methodId, void *methodContext,
  460. const UA_NodeId *objectId, void *objectContext,
  461. size_t inputSize, const UA_Variant *input,
  462. size_t outputSize, UA_Variant *output){
  463. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  464. UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
  465. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  466. retVal |= UA_Server_deleteReference(server, nodeToRemove, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  467. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS),
  468. false);
  469. retVal |= UA_Server_deleteReference(server, nodeToRemove, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  470. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET),
  471. false);
  472. retVal |= UA_Server_deleteReference(server, nodeToRemove, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  473. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER),
  474. false);
  475. retVal |= UA_Server_deleteReference(server, nodeToRemove, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  476. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER),
  477. false);
  478. #endif
  479. retVal |= UA_Server_deleteNode(server, nodeToRemove, false);
  480. return retVal;
  481. }
  482. #endif
  483. UA_StatusCode
  484. addPublishedDataItemsRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet){
  485. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  486. if(publishedDataSet->config.name.length > 512)
  487. return UA_STATUSCODE_BADOUTOFMEMORY;
  488. UA_STACKARRAY(char, pdsName, sizeof(char) * publishedDataSet->config.name.length +1);
  489. memcpy(pdsName, publishedDataSet->config.name.data, publishedDataSet->config.name.length);
  490. pdsName[publishedDataSet->config.name.length] = '\0';
  491. //This code block must use a lock
  492. UA_Nodestore_removeNode(server->nsCtx, &publishedDataSet->identifier);
  493. retVal |= addPubSubObjectNode(server, pdsName, publishedDataSet->identifier.identifier.numeric,
  494. UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS,
  495. UA_NS0ID_HASPROPERTY, UA_NS0ID_PUBLISHEDDATAITEMSTYPE);
  496. //End lock zone
  497. UA_NodeId configurationVersionNode, publishedDataNode;
  498. configurationVersionNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConfigurationVersion"),
  499. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  500. UA_NODEID_NUMERIC(0, publishedDataSet->identifier.identifier.numeric));
  501. if (UA_NodeId_equal(&configurationVersionNode, &UA_NODEID_NULL)) {
  502. return UA_STATUSCODE_BADNOTFOUND;
  503. }
  504. UA_Variant value;
  505. UA_Variant_init(&value);
  506. UA_Variant_setScalar(&value, &publishedDataSet->dataSetMetaData.configurationVersion,
  507. &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE]);
  508. retVal |= UA_Server_writeValue(server, configurationVersionNode, value);
  509. publishedDataNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishedData"),
  510. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  511. UA_NODEID_NUMERIC(0, publishedDataSet->identifier.identifier.numeric));
  512. if (UA_NodeId_equal(&publishedDataNode, &UA_NODEID_NULL)) {
  513. return UA_STATUSCODE_BADNOTFOUND;
  514. }
  515. UA_NodePropertyContext * publishingIntervalContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext));
  516. publishingIntervalContext->parentNodeId = publishedDataSet->identifier;
  517. publishingIntervalContext->parentClassifier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE;
  518. publishingIntervalContext->elementClassiefier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE_PUBLISHEDDATA;
  519. UA_ValueCallback valueCallback;
  520. valueCallback.onRead = onRead;
  521. valueCallback.onWrite = NULL;
  522. retVal |= addVariableValueSource(server, valueCallback, publishedDataNode, publishingIntervalContext);
  523. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  524. retVal |= UA_Server_addReference(server, publishedDataSet->identifier,
  525. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  526. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_ADDVARIABLES), true);
  527. retVal |= UA_Server_addReference(server, publishedDataSet->identifier,
  528. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  529. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_REMOVEVARIABLES), true);
  530. #endif
  531. return retVal;
  532. }
  533. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  534. static UA_StatusCode
  535. addPublishedDataItemsAction(UA_Server *server,
  536. const UA_NodeId *sessionId, void *sessionHandle,
  537. const UA_NodeId *methodId, void *methodContext,
  538. const UA_NodeId *objectId, void *objectContext,
  539. size_t inputSize, const UA_Variant *input,
  540. size_t outputSize, UA_Variant *output){
  541. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  542. size_t fieldNameAliasesSize = input[1].arrayLength;
  543. UA_String * fieldNameAliases = (UA_String *) input[1].data;
  544. size_t fieldFlagsSize = input[2].arrayLength;
  545. UA_DataSetFieldFlags * fieldFlags = (UA_DataSetFieldFlags *) input[2].data;
  546. size_t variablesToAddSize = input[3].arrayLength;
  547. UA_PublishedVariableDataType *variablesToAddField = (UA_PublishedVariableDataType *) input[3].data;
  548. if(!(fieldNameAliasesSize == fieldFlagsSize || fieldFlagsSize == variablesToAddSize))
  549. return UA_STATUSCODE_BADINVALIDARGUMENT;
  550. UA_PublishedDataSetConfig publishedDataSetConfig;
  551. memset(&publishedDataSetConfig, 0, sizeof(publishedDataSetConfig));
  552. publishedDataSetConfig.name = *((UA_String *) input[0].data);
  553. publishedDataSetConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
  554. UA_NodeId dataSetItemsNodeId;
  555. retVal |= UA_Server_addPublishedDataSet(server, &publishedDataSetConfig, &dataSetItemsNodeId).addResult;
  556. UA_DataSetFieldConfig dataSetFieldConfig;
  557. for (size_t j = 0; j < variablesToAddSize; ++j) {
  558. memset(&dataSetFieldConfig, 0, sizeof(dataSetFieldConfig));
  559. dataSetFieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
  560. dataSetFieldConfig.field.variable.fieldNameAlias = fieldNameAliases[j];
  561. if(fieldFlags[j] == UA_DATASETFIELDFLAGS_PROMOTEDFIELD){
  562. dataSetFieldConfig.field.variable.promotedField = UA_TRUE;
  563. }
  564. dataSetFieldConfig.field.variable.publishParameters = variablesToAddField[j];
  565. UA_Server_addDataSetField(server, dataSetItemsNodeId, &dataSetFieldConfig, NULL);
  566. }
  567. UA_PublishedVariableDataType_clear(variablesToAddField);
  568. return retVal;
  569. }
  570. #endif
  571. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  572. static UA_StatusCode
  573. addVariablesAction(UA_Server *server,
  574. const UA_NodeId *sessionId, void *sessionHandle,
  575. const UA_NodeId *methodId, void *methodContext,
  576. const UA_NodeId *objectId, void *objectContext,
  577. size_t inputSize, const UA_Variant *input,
  578. size_t outputSize, UA_Variant *output){
  579. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  580. return retVal;
  581. }
  582. static UA_StatusCode
  583. removeVariablesAction(UA_Server *server,
  584. const UA_NodeId *sessionId, void *sessionHandle,
  585. const UA_NodeId *methodId, void *methodContext,
  586. const UA_NodeId *objectId, void *objectContext,
  587. size_t inputSize, const UA_Variant *input,
  588. size_t outputSize, UA_Variant *output){
  589. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  590. return retVal;
  591. }
  592. #endif
  593. UA_StatusCode
  594. removePublishedDataSetRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet){
  595. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  596. retVal |= UA_Server_deleteNode(server, publishedDataSet->identifier, false);
  597. return retVal;
  598. }
  599. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  600. static UA_StatusCode
  601. removePublishedDataSetAction(UA_Server *server,
  602. const UA_NodeId *sessionId, void *sessionHandle,
  603. const UA_NodeId *methodId, void *methodContext,
  604. const UA_NodeId *objectId, void *objectContext,
  605. size_t inputSize, const UA_Variant *input,
  606. size_t outputSize, UA_Variant *output){
  607. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  608. UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
  609. retVal |= UA_Server_removePublishedDataSet(server, nodeToRemove);
  610. return retVal;
  611. }
  612. #endif
  613. /**********************************************/
  614. /* WriterGroup */
  615. /**********************************************/
  616. static UA_StatusCode
  617. readContentMask(UA_Server *server, const UA_NodeId *sessionId,
  618. void *sessionContext, const UA_NodeId *nodeId,
  619. void *nodeContext, UA_Boolean includeSourceTimeStamp,
  620. const UA_NumericRange *range, UA_DataValue *value) {
  621. UA_WriterGroup *writerGroup = (UA_WriterGroup*)nodeContext;
  622. if((writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED &&
  623. writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
  624. writerGroup->config.messageSettings.content.decoded.type !=
  625. &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE])
  626. return UA_STATUSCODE_BADINTERNALERROR;
  627. UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*)
  628. writerGroup->config.messageSettings.content.decoded.data;
  629. UA_Variant_setScalarCopy(&value->value, &wgm->networkMessageContentMask,
  630. &UA_TYPES[UA_TYPES_UADPNETWORKMESSAGECONTENTMASK]);
  631. value->hasValue = true;
  632. return UA_STATUSCODE_GOOD;
  633. }
  634. static UA_StatusCode
  635. writeContentMask(UA_Server *server, const UA_NodeId *sessionId,
  636. void *sessionContext, const UA_NodeId *nodeId,
  637. void *nodeContext, const UA_NumericRange *range,
  638. const UA_DataValue *value) {
  639. UA_WriterGroup *writerGroup = (UA_WriterGroup*)nodeContext;
  640. if((writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED &&
  641. writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
  642. writerGroup->config.messageSettings.content.decoded.type !=
  643. &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE])
  644. return UA_STATUSCODE_BADINTERNALERROR;
  645. UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*)
  646. writerGroup->config.messageSettings.content.decoded.data;
  647. if(!value->value.type)
  648. return UA_STATUSCODE_BADTYPEMISMATCH;
  649. if(value->value.type->typeKind != UA_DATATYPEKIND_ENUM &&
  650. value->value.type->typeKind != UA_DATATYPEKIND_INT32)
  651. return UA_STATUSCODE_BADTYPEMISMATCH;
  652. wgm->networkMessageContentMask = *(UA_UadpNetworkMessageContentMask*)value->value.data;
  653. return UA_STATUSCODE_GOOD;
  654. }
  655. UA_StatusCode
  656. addWriterGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup){
  657. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  658. if(writerGroup->config.name.length > 512)
  659. return UA_STATUSCODE_BADOUTOFMEMORY;
  660. UA_STACKARRAY(char, wgName, sizeof(char) * writerGroup->config.name.length + 1);
  661. memcpy(wgName, writerGroup->config.name.data, writerGroup->config.name.length);
  662. wgName[writerGroup->config.name.length] = '\0';
  663. //This code block must use a lock
  664. UA_Nodestore_removeNode(server->nsCtx, &writerGroup->identifier);
  665. retVal |= addPubSubObjectNode(server, wgName, writerGroup->identifier.identifier.numeric,
  666. writerGroup->linkedConnection.identifier.numeric,
  667. UA_NS0ID_HASCOMPONENT, UA_NS0ID_WRITERGROUPTYPE);
  668. //End lock zone
  669. UA_NodeId keepAliveNode, publishingIntervalNode, priorityNode, writerGroupIdNode;
  670. keepAliveNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "KeepAliveTime"),
  671. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  672. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  673. publishingIntervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"),
  674. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  675. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  676. if (UA_NodeId_equal(&keepAliveNode, &UA_NODEID_NULL) ||
  677. UA_NodeId_equal(&publishingIntervalNode, &UA_NODEID_NULL)) {
  678. return UA_STATUSCODE_BADNOTFOUND;
  679. }
  680. UA_NodePropertyContext * publishingIntervalContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext));
  681. publishingIntervalContext->parentNodeId = writerGroup->identifier;
  682. publishingIntervalContext->parentClassifier = UA_NS0ID_WRITERGROUPTYPE;
  683. publishingIntervalContext->elementClassiefier = UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL;
  684. UA_ValueCallback valueCallback;
  685. valueCallback.onRead = onRead;
  686. valueCallback.onWrite = onWrite;
  687. retVal |= addVariableValueSource(server, valueCallback, publishingIntervalNode, publishingIntervalContext);
  688. UA_Server_writeAccessLevel(server, publishingIntervalNode, (UA_ACCESSLEVELMASK_READ ^ UA_ACCESSLEVELMASK_WRITE));
  689. priorityNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Priority"),
  690. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  691. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  692. writerGroupIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "WriterGroupId"),
  693. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  694. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  695. UA_Variant value;
  696. UA_Variant_init(&value);
  697. UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]);
  698. UA_Server_writeValue(server, publishingIntervalNode, value);
  699. UA_Variant_setScalar(&value, &writerGroup->config.keepAliveTime, &UA_TYPES[UA_TYPES_DURATION]);
  700. UA_Server_writeValue(server, keepAliveNode, value);
  701. UA_Variant_setScalar(&value, &writerGroup->config.priority, &UA_TYPES[UA_TYPES_BYTE]);
  702. UA_Server_writeValue(server, priorityNode, value);
  703. UA_Variant_setScalar(&value, &writerGroup->config.writerGroupId, &UA_TYPES[UA_TYPES_UINT16]);
  704. UA_Server_writeValue(server, writerGroupIdNode, value);
  705. retVal |= addPubSubObjectNode(server, "MessageSettings", 0,
  706. writerGroup->identifier.identifier.numeric,
  707. UA_NS0ID_HASCOMPONENT, UA_NS0ID_UADPWRITERGROUPMESSAGETYPE);
  708. /* Find the variable with the content mask */
  709. UA_NodeId messageSettingsId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "MessageSettings"),
  710. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  711. UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric));
  712. UA_NodeId contentMaskId = findSingleChildNode(server,
  713. UA_QUALIFIEDNAME(0, "NetworkMessageContentMask"),
  714. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
  715. messageSettingsId);
  716. if (UA_NodeId_equal(&messageSettingsId, &UA_NODEID_NULL) ||
  717. UA_NodeId_equal(&contentMaskId, &UA_NODEID_NULL)) {
  718. return UA_STATUSCODE_BADNOTFOUND;
  719. }
  720. /* Set the callback */
  721. UA_DataSource ds;
  722. ds.read = readContentMask;
  723. ds.write = writeContentMask;
  724. UA_Server_setVariableNode_dataSource(server, contentMaskId, ds);
  725. UA_Server_setNodeContext(server, contentMaskId, writerGroup);
  726. /* Make writable */
  727. UA_Server_writeAccessLevel(server, contentMaskId, UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_READ);
  728. // UA_ACCESSLEVELTYPE_CURRENTREAD | UA_ACCESSLEVELTYPE_CURRENTWRITE);
  729. return retVal;
  730. }
  731. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  732. static UA_StatusCode
  733. addWriterGroupAction(UA_Server *server,
  734. const UA_NodeId *sessionId, void *sessionHandle,
  735. const UA_NodeId *methodId, void *methodContext,
  736. const UA_NodeId *objectId, void *objectContext,
  737. size_t inputSize, const UA_Variant *input,
  738. size_t outputSize, UA_Variant *output){
  739. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  740. UA_WriterGroupDataType *writerGroupDataType = ((UA_WriterGroupDataType *) input[0].data);
  741. UA_NodeId generatedId;
  742. UA_WriterGroupConfig writerGroupConfig;
  743. memset(&writerGroupConfig, 0, sizeof(UA_WriterGroupConfig));
  744. writerGroupConfig.name = writerGroupDataType->name;
  745. writerGroupConfig.publishingInterval = writerGroupDataType->publishingInterval;
  746. writerGroupConfig.writerGroupId = writerGroupDataType->writerGroupId;
  747. writerGroupConfig.enabled = writerGroupDataType->enabled;
  748. writerGroupConfig.priority = writerGroupDataType->priority;
  749. //TODO remove hard coded UADP
  750. writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP;
  751. //ToDo transfer all arguments to internal WGConfiguration
  752. retVal |= UA_Server_addWriterGroup(server, *objectId, &writerGroupConfig, &generatedId);
  753. UA_Variant_setScalarCopy(output, &generatedId, &UA_TYPES[UA_TYPES_NODEID]);
  754. return retVal;
  755. }
  756. #endif
  757. UA_StatusCode
  758. removeGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup) {
  759. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  760. retVal |= UA_Server_deleteNode(server, writerGroup->identifier, false);
  761. return retVal;
  762. }
  763. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  764. static UA_StatusCode
  765. removeGroupAction(UA_Server *server,
  766. const UA_NodeId *sessionId, void *sessionHandle,
  767. const UA_NodeId *methodId, void *methodContext,
  768. const UA_NodeId *objectId, void *objectContext,
  769. size_t inputSize, const UA_Variant *input,
  770. size_t outputSize, UA_Variant *output){
  771. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  772. UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
  773. if(UA_WriterGroup_findWGbyId(server, nodeToRemove) != NULL)
  774. retVal |= UA_Server_removeWriterGroup(server, nodeToRemove);
  775. //else
  776. //retVal |= UA_Server_removeReaderGroup(server, nodeToRemve);
  777. return retVal;
  778. }
  779. #endif
  780. /**********************************************/
  781. /* ReaderGroup */
  782. /**********************************************/
  783. UA_StatusCode
  784. addReaderGroupRepresentation(UA_Server *server, UA_ReaderGroup *readerGroup){
  785. //TODO implement reader part
  786. return UA_STATUSCODE_BADNOTIMPLEMENTED;
  787. }
  788. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  789. static UA_StatusCode
  790. addReaderGroupAction(UA_Server *server,
  791. const UA_NodeId *sessionId, void *sessionHandle,
  792. const UA_NodeId *methodId, void *methodContext,
  793. const UA_NodeId *objectId, void *objectContext,
  794. size_t inputSize, const UA_Variant *input,
  795. size_t outputSize, UA_Variant *output){
  796. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  797. //TODO implement reader part
  798. return retVal;
  799. }
  800. #endif
  801. /**********************************************/
  802. /* DataSetWriter */
  803. /**********************************************/
  804. UA_StatusCode
  805. addDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter){
  806. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  807. if(dataSetWriter->config.name.length > 512)
  808. return UA_STATUSCODE_BADOUTOFMEMORY;
  809. UA_STACKARRAY(char, dswName, sizeof(char) * dataSetWriter->config.name.length + 1);
  810. memcpy(dswName, dataSetWriter->config.name.data, dataSetWriter->config.name.length);
  811. dswName[dataSetWriter->config.name.length] = '\0';
  812. //This code block must use a lock
  813. UA_Nodestore_removeNode(server->nsCtx, &dataSetWriter->identifier);
  814. retVal |= addPubSubObjectNode(server, dswName, dataSetWriter->identifier.identifier.numeric,
  815. dataSetWriter->linkedWriterGroup.identifier.numeric,
  816. UA_NS0ID_HASDATASETWRITER, UA_NS0ID_DATASETWRITERTYPE);
  817. //End lock zone
  818. retVal |= UA_Server_addReference(server, dataSetWriter->connectedDataSet,
  819. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETTOWRITER),
  820. UA_EXPANDEDNODEID_NUMERIC(0, dataSetWriter->identifier.identifier.numeric), true);
  821. retVal |= addPubSubObjectNode(server, "MessageSettings", 0,
  822. dataSetWriter->identifier.identifier.numeric,
  823. UA_NS0ID_HASCOMPONENT, UA_NS0ID_UADPDATASETWRITERMESSAGETYPE);
  824. return retVal;
  825. }
  826. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  827. static UA_StatusCode
  828. addDataSetWriterAction(UA_Server *server,
  829. const UA_NodeId *sessionId, void *sessionHandle,
  830. const UA_NodeId *methodId, void *methodContext,
  831. const UA_NodeId *objectId, void *objectContext,
  832. size_t inputSize, const UA_Variant *input,
  833. size_t outputSize, UA_Variant *output){
  834. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  835. UA_DataSetWriterDataType *dataSetWriterDataType = (UA_DataSetWriterDataType *) input[0].data;
  836. UA_NodeId targetPDS = UA_NODEID_NULL;
  837. for (size_t i = 0; i < server->pubSubManager.publishedDataSetsSize; ++i) {
  838. if(UA_String_equal(&dataSetWriterDataType->dataSetName, &server->pubSubManager.publishedDataSets[i].config.name)){
  839. targetPDS = server->pubSubManager.publishedDataSets[i].identifier;
  840. }
  841. }
  842. if(UA_NodeId_isNull(&targetPDS)){
  843. return UA_STATUSCODE_BADPARENTNODEIDINVALID;
  844. }
  845. UA_NodeId generatedId;
  846. UA_DataSetWriterConfig dataSetWriterConfig;
  847. memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig));
  848. dataSetWriterConfig.name = dataSetWriterDataType->name;
  849. dataSetWriterConfig.dataSetName = dataSetWriterDataType->dataSetName;
  850. dataSetWriterConfig.keyFrameCount = dataSetWriterDataType->keyFrameCount;
  851. dataSetWriterConfig.dataSetWriterId = dataSetWriterDataType->dataSetWriterId;
  852. UA_Server_addDataSetWriter(server, *objectId, targetPDS, &dataSetWriterConfig, &generatedId);
  853. UA_Variant_setScalarCopy(output, &generatedId, &UA_TYPES[UA_TYPES_NODEID]);
  854. return retVal;
  855. }
  856. #endif
  857. UA_StatusCode
  858. removeDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter) {
  859. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  860. retVal |= UA_Server_deleteNode(server, dataSetWriter->identifier, false);
  861. return retVal;
  862. }
  863. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  864. static UA_StatusCode
  865. removeDataSetWriterAction(UA_Server *server,
  866. const UA_NodeId *sessionId, void *sessionHandle,
  867. const UA_NodeId *methodId, void *methodContext,
  868. const UA_NodeId *objectId, void *objectContext,
  869. size_t inputSize, const UA_Variant *input,
  870. size_t outputSize, UA_Variant *output){
  871. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  872. UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data);
  873. retVal |= UA_Server_removeDataSetWriter(server, nodeToRemove);
  874. return retVal;
  875. }
  876. #endif
  877. /**********************************************/
  878. /* Destructors */
  879. /**********************************************/
  880. static void
  881. connectionTypeDestructor(UA_Server *server,
  882. const UA_NodeId *sessionId, void *sessionContext,
  883. const UA_NodeId *typeId, void *typeContext,
  884. const UA_NodeId *nodeId, void **nodeContext) {
  885. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "Connection destructor called!");
  886. UA_NodeId publisherIdNode;
  887. publisherIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"),
  888. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId);
  889. UA_NodePropertyContext *internalConnectionContext;
  890. UA_Server_getNodeContext(server, publisherIdNode, (void **) &internalConnectionContext);
  891. if(!UA_NodeId_equal(&UA_NODEID_NULL , &publisherIdNode)){
  892. UA_free(internalConnectionContext);
  893. }
  894. }
  895. static void
  896. writerGroupTypeDestructor(UA_Server *server,
  897. const UA_NodeId *sessionId, void *sessionContext,
  898. const UA_NodeId *typeId, void *typeContext,
  899. const UA_NodeId *nodeId, void **nodeContext) {
  900. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "WriterGroup destructor called!");
  901. UA_NodeId intervalNode;
  902. intervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"),
  903. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId);
  904. UA_NodePropertyContext *internalConnectionContext;
  905. UA_Server_getNodeContext(server, intervalNode, (void **) &internalConnectionContext);
  906. if(!UA_NodeId_equal(&UA_NODEID_NULL , &intervalNode)){
  907. UA_free(internalConnectionContext);
  908. }
  909. }
  910. static void
  911. readerGroupTypeDestructor(UA_Server *server,
  912. const UA_NodeId *sessionId, void *sessionContext,
  913. const UA_NodeId *typeId, void *typeContext,
  914. const UA_NodeId *nodeId, void **nodeContext) {
  915. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "ReaderGroup destructor called!");
  916. }
  917. static void
  918. dataSetWriterTypeDestructor(UA_Server *server,
  919. const UA_NodeId *sessionId, void *sessionContext,
  920. const UA_NodeId *typeId, void *typeContext,
  921. const UA_NodeId *nodeId, void **nodeContext) {
  922. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "DataSetWriter destructor called!");
  923. }
  924. static void
  925. dataSetReaderTypeDestructor(UA_Server *server,
  926. const UA_NodeId *sessionId, void *sessionContext,
  927. const UA_NodeId *typeId, void *typeContext,
  928. const UA_NodeId *nodeId, void **nodeContext) {
  929. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "DataSetReader destructor called!");
  930. }
  931. static void
  932. publishedDataItemsTypeDestructor(UA_Server *server,
  933. const UA_NodeId *sessionId, void *sessionContext,
  934. const UA_NodeId *typeId, void *typeContext,
  935. const UA_NodeId *nodeId, void **nodeContext) {
  936. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "PublishedDataItems destructor called!");
  937. UA_NodeId intervalNode;
  938. UA_NodePropertyContext *internalConnectionContext;
  939. intervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishedData"),
  940. UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId);
  941. UA_Server_getNodeContext(server, intervalNode, (void **) &internalConnectionContext);
  942. if(!UA_NodeId_equal(&UA_NODEID_NULL , &intervalNode)){
  943. UA_free(internalConnectionContext);
  944. }
  945. }
  946. UA_StatusCode
  947. UA_Server_initPubSubNS0(UA_Server *server) {
  948. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  949. UA_String profileArray[1];
  950. profileArray[0] = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
  951. retVal |= writePubSubNs0VariableArray(server, UA_NS0ID_PUBLISHSUBSCRIBE_SUPPORTEDTRANSPORTPROFILES,
  952. profileArray,
  953. 1, &UA_TYPES[UA_TYPES_STRING]);
  954. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
  955. retVal |= UA_Server_setMethodNode_callback(server,
  956. UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_ADDCONNECTION), addPubSubConnectionAction);
  957. retVal |= UA_Server_setMethodNode_callback(server,
  958. UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_REMOVECONNECTION), removeConnectionAction);
  959. retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
  960. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  961. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER), true);
  962. retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
  963. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  964. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), true);
  965. retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
  966. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  967. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), true);
  968. retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS),
  969. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
  970. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER), true);
  971. retVal |= UA_Server_setMethodNode_callback(server,
  972. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER), addDataSetFolderAction);
  973. retVal |= UA_Server_setMethodNode_callback(server,
  974. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER), removeDataSetFolderAction);
  975. retVal |= UA_Server_setMethodNode_callback(server,
  976. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), addPublishedDataItemsAction);
  977. retVal |= UA_Server_setMethodNode_callback(server,
  978. UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), removePublishedDataSetAction);
  979. retVal |= UA_Server_setMethodNode_callback(server,
  980. UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_ADDVARIABLES), addVariablesAction);
  981. retVal |= UA_Server_setMethodNode_callback(server,
  982. UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_REMOVEVARIABLES), removeVariablesAction);
  983. retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDWRITERGROUP), addWriterGroupAction);
  984. retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDREADERGROUP), addReaderGroupAction);
  985. retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_REMOVEGROUP), removeGroupAction);
  986. retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE_ADDDATASETWRITER), addDataSetWriterAction);
  987. retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE_REMOVEDATASETWRITER), removeDataSetWriterAction);
  988. retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE_ADDDATASETREADER), addDataSetReaderAction);
  989. retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE_REMOVEDATASETREADER), removeDataSetReaderAction);
  990. #else
  991. retVal |= UA_Server_deleteReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  992. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_ADDCONNECTION),
  993. false);
  994. retVal |= UA_Server_deleteReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true,
  995. UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_REMOVECONNECTION),
  996. false);
  997. #endif
  998. UA_NodeTypeLifecycle liveCycle;
  999. liveCycle.constructor = NULL;
  1000. liveCycle.destructor = connectionTypeDestructor;
  1001. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE), liveCycle);
  1002. liveCycle.destructor = writerGroupTypeDestructor;
  1003. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE), liveCycle);
  1004. liveCycle.destructor = readerGroupTypeDestructor;
  1005. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE), liveCycle);
  1006. liveCycle.destructor = dataSetWriterTypeDestructor;
  1007. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETWRITERDATATYPE), liveCycle);
  1008. liveCycle.destructor = publishedDataItemsTypeDestructor;
  1009. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE), liveCycle);
  1010. liveCycle.destructor = dataSetReaderTypeDestructor;
  1011. UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETREADERDATATYPE), liveCycle);
  1012. return retVal;
  1013. }
  1014. #endif /* UA_ENABLE_PUBSUB_INFORMATIONMODEL */