ua_pubsub_ns0.c 55 KB

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