ua_pubsub_ns0.c 59 KB

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