ua_pubsub_ns0.c 55 KB

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