ua_services_nodemanagement.c 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416
  1. #include "ua_server_internal.h"
  2. #include "ua_services.h"
  3. /**********************/
  4. /* Consistency Checks */
  5. /**********************/
  6. /* Check if the requested parent node exists, has the right node class and is
  7. * referenced with an allowed (hierarchical) reference type. For "type" nodes,
  8. * only hasSubType references are allowed. */
  9. static UA_StatusCode
  10. checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
  11. const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
  12. /* See if the parent exists */
  13. const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
  14. if(!parent) {
  15. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  16. "AddNodes: Parent node not found");
  17. return UA_STATUSCODE_BADPARENTNODEIDINVALID;
  18. }
  19. /* Check the referencetype exists */
  20. const UA_ReferenceTypeNode *referenceType =
  21. (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
  22. if(!referenceType) {
  23. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  24. "AddNodes: Reference type to the parent not found");
  25. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  26. }
  27. /* Check if the referencetype is a reference type node */
  28. if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
  29. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  30. "AddNodes: Reference type to the parent invalid");
  31. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  32. }
  33. /* Check that the reference type is not abstract */
  34. if(referenceType->isAbstract == true) {
  35. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  36. "AddNodes: Abstract reference type to the parent invalid");
  37. return UA_STATUSCODE_BADREFERENCENOTALLOWED;
  38. }
  39. /* Check hassubtype relation for type nodes */
  40. const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
  41. if(nodeClass == UA_NODECLASS_DATATYPE ||
  42. nodeClass == UA_NODECLASS_VARIABLETYPE ||
  43. nodeClass == UA_NODECLASS_OBJECTTYPE ||
  44. nodeClass == UA_NODECLASS_REFERENCETYPE) {
  45. /* type needs hassubtype reference to the supertype */
  46. if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
  47. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  48. "AddNodes: New type node need to have a "
  49. "hassubtype reference");
  50. return UA_STATUSCODE_BADREFERENCENOTALLOWED;
  51. }
  52. /* supertype needs to be of the same node type */
  53. if(parent->nodeClass != nodeClass) {
  54. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  55. "AddNodes: New type node needs to be of the same "
  56. "node type as the parent");
  57. return UA_STATUSCODE_BADPARENTNODEIDINVALID;
  58. }
  59. return UA_STATUSCODE_GOOD;
  60. }
  61. /* Test if the referencetype is hierarchical */
  62. const UA_NodeId hierarchicalReference =
  63. UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
  64. if(!isNodeInTree(server->nodestore, referenceTypeId,
  65. &hierarchicalReference, &subtypeId, 1)) {
  66. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  67. "AddNodes: Reference type is not hierarchical");
  68. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  69. }
  70. return UA_STATUSCODE_GOOD;
  71. }
  72. /************/
  73. /* Add Node */
  74. /************/
  75. static void
  76. Service_AddNodes_single(UA_Server *server, UA_Session *session,
  77. const UA_AddNodesItem *item, UA_AddNodesResult *result,
  78. UA_InstantiationCallback *instantiationCallback);
  79. static UA_StatusCode
  80. copyChildNodesToNode(UA_Server *server, UA_Session *session,
  81. const UA_NodeId *sourceNodeId, const UA_NodeId *destinationNodeId,
  82. UA_InstantiationCallback *instantiationCallback);
  83. /* copy an existing variable under the given parent. then instantiate the
  84. * variable for its type */
  85. static UA_StatusCode
  86. copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *variable,
  87. const UA_NodeId *referenceType, const UA_NodeId *parent,
  88. UA_InstantiationCallback *instantiationCallback) {
  89. const UA_VariableNode *node =
  90. (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, variable);
  91. if(!node)
  92. return UA_STATUSCODE_BADNODEIDINVALID;
  93. if(node->nodeClass != UA_NODECLASS_VARIABLE)
  94. return UA_STATUSCODE_BADNODECLASSINVALID;
  95. /* Get the current value */
  96. UA_DataValue value;
  97. UA_DataValue_init(&value);
  98. UA_StatusCode retval = readValueAttribute(server, node, &value);
  99. if(retval != UA_STATUSCODE_GOOD)
  100. return retval;
  101. /* Prepare the variable description */
  102. UA_VariableAttributes attr;
  103. UA_VariableAttributes_init(&attr);
  104. attr.displayName = node->displayName;
  105. attr.description = node->description;
  106. attr.writeMask = node->writeMask;
  107. attr.value = value.value;
  108. attr.dataType = node->dataType;
  109. attr.valueRank = node->valueRank;
  110. attr.arrayDimensionsSize = node->arrayDimensionsSize;
  111. attr.arrayDimensions = node->arrayDimensions;
  112. attr.accessLevel = node->accessLevel;
  113. attr.minimumSamplingInterval = node->minimumSamplingInterval;
  114. attr.historizing = node->historizing;
  115. UA_AddNodesItem item;
  116. UA_AddNodesItem_init(&item);
  117. item.nodeClass = UA_NODECLASS_VARIABLE;
  118. item.parentNodeId.nodeId = *parent;
  119. item.referenceTypeId = *referenceType;
  120. item.browseName = node->browseName;
  121. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  122. item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES];
  123. item.nodeAttributes.content.decoded.data = &attr;
  124. const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)getNodeType(server, (const UA_Node*)node);
  125. if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE || vt->isAbstract) {
  126. retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  127. goto cleanup;
  128. }
  129. item.typeDefinition.nodeId = vt->nodeId;
  130. /* Add the variable and instantiate the children */
  131. UA_AddNodesResult res;
  132. UA_AddNodesResult_init(&res);
  133. Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
  134. if(res.statusCode != UA_STATUSCODE_GOOD) {
  135. retval = res.statusCode;
  136. goto cleanup;
  137. }
  138. retval = copyChildNodesToNode(server, session, &node->nodeId,
  139. &res.addedNodeId, instantiationCallback);
  140. if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
  141. instantiationCallback->method(res.addedNodeId, node->nodeId,
  142. instantiationCallback->handle);
  143. UA_NodeId_deleteMembers(&res.addedNodeId);
  144. cleanup:
  145. if(value.hasValue && value.value.storageType == UA_VARIANT_DATA)
  146. UA_Variant_deleteMembers(&value.value);
  147. return retval;
  148. }
  149. /* Copy an existing object under the given parent. Then instantiate for all
  150. * hastypedefinitions of the original version. */
  151. static UA_StatusCode
  152. copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *object,
  153. const UA_NodeId *referenceType, const UA_NodeId *parent,
  154. UA_InstantiationCallback *instantiationCallback) {
  155. const UA_ObjectNode *node =
  156. (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, object);
  157. if(!node)
  158. return UA_STATUSCODE_BADNODEIDINVALID;
  159. if(node->nodeClass != UA_NODECLASS_OBJECT)
  160. return UA_STATUSCODE_BADNODECLASSINVALID;
  161. /* Prepare the item */
  162. UA_ObjectAttributes attr;
  163. UA_ObjectAttributes_init(&attr);
  164. attr.displayName = node->displayName;
  165. attr.description = node->description;
  166. attr.writeMask = node->writeMask;
  167. attr.eventNotifier = node->eventNotifier;
  168. UA_AddNodesItem item;
  169. UA_AddNodesItem_init(&item);
  170. item.nodeClass = UA_NODECLASS_OBJECT;
  171. item.parentNodeId.nodeId = *parent;
  172. item.referenceTypeId = *referenceType;
  173. item.browseName = node->browseName;
  174. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  175. item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES];
  176. item.nodeAttributes.content.decoded.data = &attr;
  177. const UA_ObjectTypeNode *objtype = (const UA_ObjectTypeNode*)getNodeType(server, (const UA_Node*)node);
  178. if(!objtype || objtype->nodeClass != UA_NODECLASS_OBJECTTYPE || objtype->isAbstract)
  179. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  180. item.typeDefinition.nodeId = objtype->nodeId;
  181. /* add the new object */
  182. UA_AddNodesResult res;
  183. UA_AddNodesResult_init(&res);
  184. Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
  185. if(res.statusCode != UA_STATUSCODE_GOOD)
  186. return res.statusCode;
  187. /* Copy any aggregated/nested variables/methods/subobjects this object contains
  188. * These objects may not be part of the nodes type. */
  189. UA_StatusCode retval = copyChildNodesToNode(server, session, &node->nodeId,
  190. &res.addedNodeId, instantiationCallback);
  191. if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
  192. instantiationCallback->method(res.addedNodeId, node->nodeId,
  193. instantiationCallback->handle);
  194. UA_NodeId_deleteMembers(&res.addedNodeId);
  195. return retval;
  196. }
  197. static UA_StatusCode
  198. setObjectInstanceHandle(UA_Server *server, UA_Session *session,
  199. UA_ObjectNode* node, void * (*constructor)(const UA_NodeId instance)) {
  200. if(node->nodeClass != UA_NODECLASS_OBJECT)
  201. return UA_STATUSCODE_BADNODECLASSINVALID;
  202. if(!node->instanceHandle)
  203. node->instanceHandle = constructor(node->nodeId);
  204. return UA_STATUSCODE_GOOD;
  205. }
  206. static UA_StatusCode
  207. instantiateNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
  208. UA_NodeClass nodeClass, const UA_NodeId *typeId,
  209. UA_InstantiationCallback *instantiationCallback) {
  210. /* see if the type node is correct */
  211. const UA_Node *typenode = UA_NodeStore_get(server->nodestore, typeId);
  212. if(!typenode)
  213. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  214. if(nodeClass == UA_NODECLASS_VARIABLE) {
  215. if(typenode->nodeClass != UA_NODECLASS_VARIABLETYPE ||
  216. ((const UA_VariableTypeNode*)typenode)->isAbstract)
  217. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  218. } else if(nodeClass == UA_NODECLASS_OBJECT) {
  219. if(typenode->nodeClass != UA_NODECLASS_OBJECTTYPE ||
  220. ((const UA_ObjectTypeNode*)typenode)->isAbstract)
  221. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  222. } else {
  223. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  224. }
  225. /* Get the hierarchy of the type and all its supertypes */
  226. UA_NodeId *hierarchy = NULL;
  227. size_t hierarchySize = 0;
  228. UA_StatusCode retval =
  229. getTypeHierarchy(server->nodestore, typenode, true, &hierarchy, &hierarchySize);
  230. if(retval != UA_STATUSCODE_GOOD)
  231. return retval;
  232. /* Copy members of the type and supertypes */
  233. for(size_t i = 0; i < hierarchySize; ++i)
  234. retval |= copyChildNodesToNode(server, session, &hierarchy[i], nodeId, instantiationCallback);
  235. UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]);
  236. if(retval != UA_STATUSCODE_GOOD)
  237. return retval;
  238. /* Call the object constructor */
  239. if(typenode->nodeClass == UA_NODECLASS_OBJECTTYPE) {
  240. const UA_ObjectLifecycleManagement *olm =
  241. &((const UA_ObjectTypeNode*)typenode)->lifecycleManagement;
  242. if(olm->constructor)
  243. UA_Server_editNode(server, session, nodeId,
  244. (UA_EditNodeCallback)setObjectInstanceHandle,
  245. (void*)olm->constructor);
  246. }
  247. /* Add a hasType reference */
  248. UA_AddReferencesItem addref;
  249. UA_AddReferencesItem_init(&addref);
  250. addref.sourceNodeId = *nodeId;
  251. addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
  252. addref.isForward = true;
  253. addref.targetNodeId.nodeId = *typeId;
  254. return Service_AddReferences_single(server, session, &addref);
  255. }
  256. /* Search for an instance of "browseName" in node searchInstance
  257. * Used during copyChildNodes to find overwritable/mergable nodes */
  258. static UA_StatusCode
  259. instanceFindAggregateByBrowsename(UA_Server *server, UA_Session *session,
  260. const UA_NodeId *searchInstance,
  261. const UA_QualifiedName *browseName,
  262. UA_NodeId *outInstanceNodeId) {
  263. UA_BrowseDescription bd;
  264. UA_BrowseDescription_init(&bd);
  265. bd.nodeId = *searchInstance;
  266. bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
  267. bd.includeSubtypes = true;
  268. bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  269. bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
  270. bd.resultMask = UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME;
  271. UA_BrowseResult br;
  272. UA_BrowseResult_init(&br);
  273. Service_Browse_single(server, session, NULL, &bd, 0, &br);
  274. if(br.statusCode != UA_STATUSCODE_GOOD)
  275. return br.statusCode;
  276. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  277. for(size_t i = 0; i < br.referencesSize; ++i) {
  278. UA_ReferenceDescription *rd = &br.references[i];
  279. if(rd->browseName.namespaceIndex == browseName->namespaceIndex &&
  280. UA_String_equal(&rd->browseName.name, &browseName->name)) {
  281. retval = UA_NodeId_copy(&rd->nodeId.nodeId, outInstanceNodeId);
  282. break;
  283. }
  284. }
  285. UA_BrowseResult_deleteMembers(&br);
  286. return retval;
  287. }
  288. /* Copy any children of Node sourceNodeId to another node destinationNodeId
  289. * Used at 2 places:
  290. * (1) During instantiation, when any children of the Type are copied
  291. * (2) During instantiation to copy any *nested* instances to the new node
  292. * (2.1) Might call instantiation of a type first
  293. * (2.2) *Should* then overwrite nested contents in definition --> this scenario is currently not handled!
  294. */
  295. static UA_StatusCode
  296. copyChildNodesToNode(UA_Server* server, UA_Session* session,
  297. const UA_NodeId* sourceNodeId, const UA_NodeId* destinationNodeId,
  298. UA_InstantiationCallback* instantiationCallback) {
  299. /* Browse to get all children */
  300. UA_BrowseDescription bd;
  301. UA_BrowseDescription_init(&bd);
  302. bd.nodeId = *sourceNodeId;
  303. bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
  304. bd.includeSubtypes = true;
  305. bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  306. bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
  307. bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS |
  308. UA_BROWSERESULTMASK_BROWSENAME;
  309. UA_BrowseResult br;
  310. UA_BrowseResult_init(&br);
  311. Service_Browse_single(server, session, NULL, &bd, 0, &br);
  312. if(br.statusCode != UA_STATUSCODE_GOOD)
  313. return br.statusCode;
  314. /* Copy all children */
  315. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  316. UA_NodeId existingChild = UA_NODEID_NULL;
  317. for(size_t i = 0; i < br.referencesSize; ++i) {
  318. UA_ReferenceDescription *rd = &br.references[i];
  319. // Check for deduplication
  320. retval = instanceFindAggregateByBrowsename(server, session, destinationNodeId,
  321. &rd->browseName, &existingChild);
  322. if(retval != UA_STATUSCODE_GOOD)
  323. break;
  324. if(UA_NodeId_equal(&UA_NODEID_NULL, &existingChild)) {
  325. /* New node in child */
  326. if(rd->nodeClass == UA_NODECLASS_METHOD) {
  327. /* add a reference to the method in the objecttype */
  328. UA_AddReferencesItem newItem;
  329. UA_AddReferencesItem_init(&newItem);
  330. newItem.sourceNodeId = *destinationNodeId;
  331. newItem.referenceTypeId = rd->referenceTypeId;
  332. newItem.isForward = true;
  333. newItem.targetNodeId = rd->nodeId;
  334. newItem.targetNodeClass = UA_NODECLASS_METHOD;
  335. retval = Service_AddReferences_single(server, session, &newItem);
  336. } else if(rd->nodeClass == UA_NODECLASS_VARIABLE)
  337. retval = copyExistingVariable(server, session, &rd->nodeId.nodeId,
  338. &rd->referenceTypeId, destinationNodeId,
  339. instantiationCallback);
  340. else if(rd->nodeClass == UA_NODECLASS_OBJECT)
  341. retval = copyExistingObject(server, session, &rd->nodeId.nodeId,
  342. &rd->referenceTypeId, destinationNodeId,
  343. instantiationCallback);
  344. } else {
  345. /* Preexistent node in child
  346. * General strategy if we meet an already existing node:
  347. * - Preexistent variable contents always 'win' overwriting anything
  348. * supertypes would instantiate
  349. * - Always copy contents of template *into* existant node (merge
  350. * contents of e.g. Folders like ParameterSet) */
  351. if(rd->nodeClass == UA_NODECLASS_METHOD) {
  352. /* Do nothing, existent method wins */
  353. } else if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
  354. rd->nodeClass == UA_NODECLASS_OBJECT) {
  355. if(!UA_NodeId_equal(&rd->nodeId.nodeId, &existingChild))
  356. retval = copyChildNodesToNode(server, session, &rd->nodeId.nodeId,
  357. &existingChild, instantiationCallback);
  358. }
  359. UA_NodeId_deleteMembers(&existingChild);
  360. }
  361. if(retval != UA_STATUSCODE_GOOD)
  362. break;
  363. }
  364. UA_BrowseResult_deleteMembers(&br);
  365. return retval;
  366. }
  367. UA_StatusCode
  368. Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
  369. const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
  370. const UA_NodeId *typeDefinition,
  371. UA_InstantiationCallback *instantiationCallback,
  372. UA_NodeId *addedNodeId) {
  373. /* Check the namespaceindex */
  374. if(node->nodeId.namespaceIndex >= server->namespacesSize) {
  375. UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: Namespace invalid");
  376. UA_NodeStore_deleteNode(node);
  377. return UA_STATUSCODE_BADNODEIDINVALID;
  378. }
  379. /* Check the reference to the parent */
  380. UA_StatusCode retval = checkParentReference(server, session, node->nodeClass,
  381. parentNodeId, referenceTypeId);
  382. if(retval != UA_STATUSCODE_GOOD) {
  383. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  384. "AddNodes: Checking the reference to the parent returned"
  385. "error code %s", UA_StatusCode_name(retval));
  386. UA_NodeStore_deleteNode(node);
  387. return retval;
  388. }
  389. /* Add the node to the nodestore */
  390. retval = UA_NodeStore_insert(server->nodestore, node);
  391. if(retval != UA_STATUSCODE_GOOD) {
  392. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  393. "AddNodes: Node could not be added to the nodestore "
  394. "with error code %s", UA_StatusCode_name(retval));
  395. return retval;
  396. }
  397. /* Copy the nodeid if needed */
  398. if(addedNodeId) {
  399. retval = UA_NodeId_copy(&node->nodeId, addedNodeId);
  400. if(retval != UA_STATUSCODE_GOOD) {
  401. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  402. "AddNodes: Could not copy the nodeid");
  403. goto remove_node;
  404. }
  405. }
  406. /* Hierarchical reference back to the parent */
  407. UA_AddReferencesItem item;
  408. UA_AddReferencesItem_init(&item);
  409. item.sourceNodeId = node->nodeId;
  410. item.referenceTypeId = *referenceTypeId;
  411. item.isForward = false;
  412. item.targetNodeId.nodeId = *parentNodeId;
  413. retval = Service_AddReferences_single(server, session, &item);
  414. if(retval != UA_STATUSCODE_GOOD) {
  415. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  416. "AddNodes: Could not add the reference to the parent"
  417. "with error code %s", UA_StatusCode_name(retval));
  418. goto remove_node;
  419. }
  420. if(node->nodeClass == UA_NODECLASS_VARIABLE ||
  421. node->nodeClass == UA_NODECLASS_OBJECT) {
  422. /* Fall back to a default typedefinition for variables and objects */
  423. const UA_NodeId basedatavariabletype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  424. const UA_NodeId baseobjecttype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
  425. if(!typeDefinition || UA_NodeId_isNull(typeDefinition)) {
  426. if(node->nodeClass == UA_NODECLASS_VARIABLE)
  427. typeDefinition = &basedatavariabletype;
  428. else
  429. typeDefinition = &baseobjecttype;
  430. }
  431. /* Instantiate variables and objects */
  432. retval = instantiateNode(server, session, &node->nodeId, node->nodeClass,
  433. typeDefinition, instantiationCallback);
  434. if(retval != UA_STATUSCODE_GOOD) {
  435. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  436. "AddNodes: Could not instantiate the node with"
  437. "error code 0x%08x", retval);
  438. goto remove_node;
  439. }
  440. }
  441. /* Custom callback */
  442. if(instantiationCallback)
  443. instantiationCallback->method(node->nodeId, *typeDefinition,
  444. instantiationCallback->handle);
  445. return UA_STATUSCODE_GOOD;
  446. remove_node:
  447. Service_DeleteNodes_single(server, &adminSession, &node->nodeId, true);
  448. return retval;
  449. }
  450. /*******************************************/
  451. /* Create nodes from attribute description */
  452. /*******************************************/
  453. static UA_StatusCode
  454. copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item,
  455. const UA_NodeAttributes *attr) {
  456. UA_StatusCode retval;
  457. retval = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId);
  458. retval |= UA_QualifiedName_copy(&item->browseName, &node->browseName);
  459. retval |= UA_LocalizedText_copy(&attr->displayName, &node->displayName);
  460. retval |= UA_LocalizedText_copy(&attr->description, &node->description);
  461. node->writeMask = attr->writeMask;
  462. return retval;
  463. }
  464. static UA_StatusCode
  465. copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
  466. const UA_AddNodesItem *item,
  467. const UA_VariableAttributes *attr) {
  468. const UA_NodeId basevartype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE);
  469. const UA_NodeId basedatavartype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  470. const UA_NodeId *typeDef = &item->typeDefinition.nodeId;
  471. if(UA_NodeId_isNull(typeDef)) /* workaround when the variabletype is undefined */
  472. typeDef = &basedatavartype;
  473. /* Make sure we can instantiate the basetypes themselves */
  474. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  475. if(UA_NodeId_equal(&node->nodeId, &basevartype) ||
  476. UA_NodeId_equal(&node->nodeId, &basedatavartype)) {
  477. node->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
  478. node->valueRank = -2;
  479. return retval;
  480. }
  481. const UA_VariableTypeNode *vt =
  482. (const UA_VariableTypeNode*)UA_NodeStore_get(server->nodestore, typeDef);
  483. if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE)
  484. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  485. if(node->nodeClass == UA_NODECLASS_VARIABLE && vt->isAbstract)
  486. return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  487. /* Set the datatype */
  488. if(!UA_NodeId_isNull(&attr->dataType))
  489. retval = writeDataTypeAttribute(server, node, &attr->dataType, &vt->dataType);
  490. else /* workaround common error where the datatype is left as NA_NODEID_NULL */
  491. retval = UA_NodeId_copy(&vt->dataType, &node->dataType);
  492. if(retval != UA_STATUSCODE_GOOD)
  493. return retval;
  494. /* Set the array dimensions. Check only against the vt. */
  495. retval = compatibleArrayDimensions(attr->arrayDimensionsSize, attr->arrayDimensions,
  496. vt->arrayDimensionsSize, vt->arrayDimensions);
  497. if(retval != UA_STATUSCODE_GOOD)
  498. return retval;
  499. retval = UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize,
  500. (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]);
  501. if(retval != UA_STATUSCODE_GOOD)
  502. return retval;
  503. node->arrayDimensionsSize = attr->arrayDimensionsSize;
  504. /* Set the valuerank */
  505. if(attr->valueRank != 0 || !UA_Variant_isScalar(&attr->value))
  506. retval = writeValueRankAttribute(server, node, attr->valueRank, vt->valueRank);
  507. else /* workaround common error where the valuerank is left as 0 */
  508. node->valueRank = vt->valueRank;
  509. if(retval != UA_STATUSCODE_GOOD)
  510. return retval;
  511. /* Set the value */
  512. UA_DataValue value;
  513. UA_DataValue_init(&value);
  514. value.value = attr->value;
  515. value.hasValue = true;
  516. retval |= writeValueAttribute(server, node, &value, NULL);
  517. return retval;
  518. }
  519. static UA_StatusCode
  520. copyVariableNodeAttributes(UA_Server *server, UA_VariableNode *vnode,
  521. const UA_AddNodesItem *item,
  522. const UA_VariableAttributes *attr) {
  523. vnode->accessLevel = attr->accessLevel;
  524. vnode->historizing = attr->historizing;
  525. vnode->minimumSamplingInterval = attr->minimumSamplingInterval;
  526. return copyCommonVariableAttributes(server, vnode, item, attr);
  527. }
  528. static UA_StatusCode
  529. copyVariableTypeNodeAttributes(UA_Server *server, UA_VariableTypeNode *vtnode,
  530. const UA_AddNodesItem *item,
  531. const UA_VariableTypeAttributes *attr) {
  532. vtnode->isAbstract = attr->isAbstract;
  533. return copyCommonVariableAttributes(server, (UA_VariableNode*)vtnode, item,
  534. (const UA_VariableAttributes*)attr);
  535. }
  536. static UA_StatusCode
  537. copyObjectNodeAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) {
  538. onode->eventNotifier = attr->eventNotifier;
  539. return UA_STATUSCODE_GOOD;
  540. }
  541. static UA_StatusCode
  542. copyReferenceTypeNodeAttributes(UA_ReferenceTypeNode *rtnode,
  543. const UA_ReferenceTypeAttributes *attr) {
  544. rtnode->isAbstract = attr->isAbstract;
  545. rtnode->symmetric = attr->symmetric;
  546. return UA_LocalizedText_copy(&attr->inverseName, &rtnode->inverseName);
  547. }
  548. static UA_StatusCode
  549. copyObjectTypeNodeAttributes(UA_ObjectTypeNode *otnode,
  550. const UA_ObjectTypeAttributes *attr) {
  551. otnode->isAbstract = attr->isAbstract;
  552. return UA_STATUSCODE_GOOD;
  553. }
  554. static UA_StatusCode
  555. copyViewNodeAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) {
  556. vnode->containsNoLoops = attr->containsNoLoops;
  557. vnode->eventNotifier = attr->eventNotifier;
  558. return UA_STATUSCODE_GOOD;
  559. }
  560. static UA_StatusCode
  561. copyDataTypeNodeAttributes(UA_DataTypeNode *dtnode,
  562. const UA_DataTypeAttributes *attr) {
  563. dtnode->isAbstract = attr->isAbstract;
  564. return UA_STATUSCODE_GOOD;
  565. }
  566. #define CHECK_ATTRIBUTES(TYPE) \
  567. if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_##TYPE]) { \
  568. retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \
  569. break; \
  570. }
  571. static UA_StatusCode
  572. createNodeFromAttributes(UA_Server *server, const UA_AddNodesItem *item, UA_Node **newNode) {
  573. /* Check that we can read the attributes */
  574. if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED ||
  575. !item->nodeAttributes.content.decoded.type)
  576. return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
  577. /* Create the node */
  578. // todo: error case where the nodeclass is faulty
  579. void *node = UA_NodeStore_newNode(item->nodeClass);
  580. if(!node)
  581. return UA_STATUSCODE_BADOUTOFMEMORY;
  582. /* Copy the attributes into the node */
  583. void *data = item->nodeAttributes.content.decoded.data;
  584. UA_StatusCode retval = copyStandardAttributes((UA_Node *)node, item, (UA_NodeAttributes *)data);
  585. switch(item->nodeClass) {
  586. case UA_NODECLASS_OBJECT:
  587. CHECK_ATTRIBUTES(OBJECTATTRIBUTES);
  588. retval |= copyObjectNodeAttributes((UA_ObjectNode *)node, (UA_ObjectAttributes *)data);
  589. break;
  590. case UA_NODECLASS_VARIABLE:
  591. CHECK_ATTRIBUTES(VARIABLEATTRIBUTES);
  592. retval |= copyVariableNodeAttributes(server, (UA_VariableNode *)node, item, (UA_VariableAttributes *)data);
  593. break;
  594. case UA_NODECLASS_OBJECTTYPE:
  595. CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES);
  596. retval |= copyObjectTypeNodeAttributes((UA_ObjectTypeNode *)node, (UA_ObjectTypeAttributes *)data);
  597. break;
  598. case UA_NODECLASS_VARIABLETYPE:
  599. CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES);
  600. retval |= copyVariableTypeNodeAttributes(server,(UA_VariableTypeNode *)node, item, (UA_VariableTypeAttributes *)data);
  601. break;
  602. case UA_NODECLASS_REFERENCETYPE:
  603. CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES);
  604. retval |= copyReferenceTypeNodeAttributes((UA_ReferenceTypeNode *)node,(const UA_ReferenceTypeAttributes *)data);
  605. break;
  606. case UA_NODECLASS_DATATYPE:
  607. CHECK_ATTRIBUTES(DATATYPEATTRIBUTES);
  608. retval |= copyDataTypeNodeAttributes((UA_DataTypeNode *)node, (UA_DataTypeAttributes *)data);
  609. break;
  610. case UA_NODECLASS_VIEW:
  611. CHECK_ATTRIBUTES(VIEWATTRIBUTES);
  612. retval |= copyViewNodeAttributes((UA_ViewNode *)node, (const UA_ViewAttributes *)data);
  613. break;
  614. case UA_NODECLASS_METHOD:
  615. case UA_NODECLASS_UNSPECIFIED:
  616. default:
  617. retval = UA_STATUSCODE_BADNODECLASSINVALID;
  618. }
  619. if(retval == UA_STATUSCODE_GOOD)
  620. *newNode = (UA_Node*)node;
  621. else
  622. UA_NodeStore_deleteNode((UA_Node*)node);
  623. return retval;
  624. }
  625. static void
  626. Service_AddNodes_single(UA_Server *server, UA_Session *session,
  627. const UA_AddNodesItem *item, UA_AddNodesResult *result,
  628. UA_InstantiationCallback *instantiationCallback) {
  629. /* Create the node from the attributes*/
  630. UA_Node *node = NULL;
  631. result->statusCode = createNodeFromAttributes(server, item, &node);
  632. if(result->statusCode != UA_STATUSCODE_GOOD)
  633. return;
  634. /* Run consistency checks and add the node */
  635. UA_assert(node != NULL);
  636. result->statusCode = Service_AddNodes_existing(server, session, node, &item->parentNodeId.nodeId,
  637. &item->referenceTypeId, &item->typeDefinition.nodeId,
  638. instantiationCallback, &result->addedNodeId);
  639. }
  640. void Service_AddNodes(UA_Server *server, UA_Session *session,
  641. const UA_AddNodesRequest *request,
  642. UA_AddNodesResponse *response) {
  643. UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing AddNodesRequest");
  644. if(request->nodesToAddSize <= 0) {
  645. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  646. return;
  647. }
  648. size_t size = request->nodesToAddSize;
  649. response->results = (UA_AddNodesResult *)UA_Array_new(size, &UA_TYPES[UA_TYPES_ADDNODESRESULT]);
  650. if(!response->results) {
  651. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  652. return;
  653. }
  654. #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
  655. #ifdef _MSC_VER
  656. UA_Boolean *isExternal = UA_alloca(size);
  657. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32)*size);
  658. #else
  659. UA_Boolean isExternal[size];
  660. UA_UInt32 indices[size];
  661. #endif
  662. memset(isExternal, false, sizeof(UA_Boolean) * size);
  663. for(size_t j = 0; j <server->externalNamespacesSize; ++j) {
  664. size_t indexSize = 0;
  665. for(size_t i = 0;i < size;++i) {
  666. if(request->nodesToAdd[i].requestedNewNodeId.nodeId.namespaceIndex !=
  667. server->externalNamespaces[j].index)
  668. continue;
  669. isExternal[i] = true;
  670. indices[indexSize] = (UA_UInt32)i;
  671. ++indexSize;
  672. }
  673. if(indexSize == 0)
  674. continue;
  675. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  676. ens->addNodes(ens->ensHandle, &request->requestHeader,
  677. request->nodesToAdd, indices, (UA_UInt32)indexSize,
  678. response->results, response->diagnosticInfos);
  679. }
  680. #endif
  681. response->resultsSize = size;
  682. for(size_t i = 0; i < size; ++i) {
  683. #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
  684. if(!isExternal[i])
  685. #endif
  686. Service_AddNodes_single(server, session, &request->nodesToAdd[i],
  687. &response->results[i], NULL);
  688. }
  689. }
  690. UA_StatusCode
  691. __UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
  692. const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
  693. const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
  694. const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
  695. const UA_DataType *attributeType,
  696. UA_InstantiationCallback *instantiationCallback, UA_NodeId *outNewNodeId) {
  697. /* prepare the item */
  698. UA_AddNodesItem item;
  699. UA_AddNodesItem_init(&item);
  700. item.parentNodeId.nodeId = parentNodeId;
  701. item.referenceTypeId = referenceTypeId;
  702. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  703. item.browseName = browseName;
  704. item.nodeClass = nodeClass;
  705. item.typeDefinition.nodeId = typeDefinition;
  706. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  707. item.nodeAttributes.content.decoded.type = attributeType;
  708. item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr;
  709. /* run the service */
  710. UA_AddNodesResult result;
  711. UA_AddNodesResult_init(&result);
  712. UA_RCU_LOCK();
  713. Service_AddNodes_single(server, &adminSession, &item, &result, instantiationCallback);
  714. UA_RCU_UNLOCK();
  715. /* prepare the output */
  716. if(outNewNodeId && result.statusCode == UA_STATUSCODE_GOOD)
  717. *outNewNodeId = result.addedNodeId;
  718. else
  719. UA_NodeId_deleteMembers(&result.addedNodeId);
  720. return result.statusCode;
  721. }
  722. /**************************************************/
  723. /* Add Special Nodes (not possible over the wire) */
  724. /**************************************************/
  725. UA_StatusCode
  726. UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
  727. const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
  728. const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
  729. const UA_VariableAttributes attr, const UA_DataSource dataSource,
  730. UA_NodeId *outNewNodeId) {
  731. /* Create the new node */
  732. UA_VariableNode *node = UA_NodeStore_newVariableNode();
  733. if(!node)
  734. return UA_STATUSCODE_BADOUTOFMEMORY;
  735. /* Read the current value (to do typechecking) */
  736. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  737. UA_VariableAttributes editAttr = attr;
  738. UA_DataValue value;
  739. UA_DataValue_init(&value);
  740. if(dataSource.read)
  741. retval = dataSource.read(dataSource.handle, requestedNewNodeId,
  742. false, NULL, &value);
  743. else
  744. retval = UA_STATUSCODE_BADTYPEMISMATCH;
  745. editAttr.value = value.value;
  746. if(retval != UA_STATUSCODE_GOOD) {
  747. UA_NodeStore_deleteNode((UA_Node*)node);
  748. return retval;
  749. }
  750. /* Copy attributes into node */
  751. UA_RCU_LOCK();
  752. UA_AddNodesItem item;
  753. UA_AddNodesItem_init(&item);
  754. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  755. item.browseName = browseName;
  756. item.typeDefinition.nodeId = typeDefinition;
  757. item.parentNodeId.nodeId = parentNodeId;
  758. retval |= copyStandardAttributes((UA_Node*)node, &item, (const UA_NodeAttributes*)&editAttr);
  759. retval |= copyVariableNodeAttributes(server, node, &item, &editAttr);
  760. UA_DataValue_deleteMembers(&node->value.data.value);
  761. node->valueSource = UA_VALUESOURCE_DATASOURCE;
  762. node->value.dataSource = dataSource;
  763. UA_DataValue_deleteMembers(&value);
  764. if(retval != UA_STATUSCODE_GOOD) {
  765. UA_NodeStore_deleteNode((UA_Node*)node);
  766. UA_RCU_UNLOCK();
  767. return retval;
  768. }
  769. /* Add the node */
  770. UA_AddNodesResult result;
  771. UA_AddNodesResult_init(&result);
  772. retval = Service_AddNodes_existing(server, &adminSession, (UA_Node*)node, &parentNodeId,
  773. &referenceTypeId, &typeDefinition, NULL, outNewNodeId);
  774. UA_RCU_UNLOCK();
  775. return retval;
  776. }
  777. #ifdef UA_ENABLE_METHODCALLS
  778. UA_StatusCode
  779. UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
  780. const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
  781. const UA_QualifiedName browseName, const UA_MethodAttributes attr,
  782. UA_MethodCallback method, void *handle,
  783. size_t inputArgumentsSize, const UA_Argument* inputArguments,
  784. size_t outputArgumentsSize, const UA_Argument* outputArguments,
  785. UA_NodeId *outNewNodeId) {
  786. UA_MethodNode *node = UA_NodeStore_newMethodNode();
  787. if(!node)
  788. return UA_STATUSCODE_BADOUTOFMEMORY;
  789. UA_AddNodesItem item;
  790. UA_AddNodesItem_init(&item);
  791. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  792. item.browseName = browseName;
  793. copyStandardAttributes((UA_Node*)node, &item, (const UA_NodeAttributes*)&attr);
  794. node->executable = attr.executable;
  795. node->attachedMethod = method;
  796. node->methodHandle = handle;
  797. /* Add the node */
  798. UA_NodeId newMethodId;
  799. UA_NodeId_init(&newMethodId);
  800. UA_RCU_LOCK();
  801. UA_StatusCode retval = Service_AddNodes_existing(server, &adminSession, (UA_Node*)node, &parentNodeId,
  802. &referenceTypeId, &UA_NODEID_NULL, NULL, &newMethodId);
  803. UA_RCU_UNLOCK();
  804. if(retval != UA_STATUSCODE_GOOD)
  805. return retval;
  806. const UA_NodeId hasproperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
  807. const UA_NodeId propertytype = UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE);
  808. if(inputArgumentsSize > 0) {
  809. UA_VariableNode *inputArgumentsVariableNode = UA_NodeStore_newVariableNode();
  810. inputArgumentsVariableNode->nodeId.namespaceIndex = newMethodId.namespaceIndex;
  811. inputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0, "InputArguments");
  812. inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
  813. inputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
  814. inputArgumentsVariableNode->valueRank = 1;
  815. /* UAExport creates a monitoreditem on inputarguments ... */
  816. inputArgumentsVariableNode->minimumSamplingInterval = 10000.0f;
  817. //TODO: 0.3 work item: the addMethodNode API does not have the possibility to set nodeIDs
  818. //actually we need to change the signature to pass UA_NS0ID_SERVER_GETMONITOREDITEMS_INPUTARGUMENTS
  819. //and UA_NS0ID_SERVER_GETMONITOREDITEMS_OUTPUTARGUMENTS into the function :/
  820. if(newMethodId.namespaceIndex == 0 &&
  821. newMethodId.identifierType == UA_NODEIDTYPE_NUMERIC &&
  822. newMethodId.identifier.numeric == UA_NS0ID_SERVER_GETMONITOREDITEMS) {
  823. inputArgumentsVariableNode->nodeId =
  824. UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS_INPUTARGUMENTS);
  825. }
  826. UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.data.value.value,
  827. inputArguments, inputArgumentsSize,
  828. &UA_TYPES[UA_TYPES_ARGUMENT]);
  829. inputArgumentsVariableNode->value.data.value.hasValue = true;
  830. UA_RCU_LOCK();
  831. // todo: check if adding succeeded
  832. Service_AddNodes_existing(server, &adminSession, (UA_Node*)inputArgumentsVariableNode,
  833. &newMethodId, &hasproperty, &propertytype, NULL, NULL);
  834. UA_RCU_UNLOCK();
  835. }
  836. if(outputArgumentsSize > 0) {
  837. /* create OutputArguments */
  838. UA_VariableNode *outputArgumentsVariableNode = UA_NodeStore_newVariableNode();
  839. outputArgumentsVariableNode->nodeId.namespaceIndex = newMethodId.namespaceIndex;
  840. outputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0, "OutputArguments");
  841. outputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
  842. outputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
  843. outputArgumentsVariableNode->valueRank = 1;
  844. //FIXME: comment in line 882
  845. if(newMethodId.namespaceIndex == 0 &&
  846. newMethodId.identifierType == UA_NODEIDTYPE_NUMERIC &&
  847. newMethodId.identifier.numeric == UA_NS0ID_SERVER_GETMONITOREDITEMS) {
  848. outputArgumentsVariableNode->nodeId =
  849. UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS_OUTPUTARGUMENTS);
  850. }
  851. UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.data.value.value,
  852. outputArguments, outputArgumentsSize,
  853. &UA_TYPES[UA_TYPES_ARGUMENT]);
  854. outputArgumentsVariableNode->value.data.value.hasValue = true;
  855. UA_RCU_LOCK();
  856. // todo: check if adding succeeded
  857. Service_AddNodes_existing(server, &adminSession, (UA_Node*)outputArgumentsVariableNode,
  858. &newMethodId, &hasproperty, &propertytype, NULL, NULL);
  859. UA_RCU_UNLOCK();
  860. }
  861. if(outNewNodeId)
  862. *outNewNodeId = newMethodId;
  863. else
  864. UA_NodeId_deleteMembers(&newMethodId);
  865. return retval;
  866. }
  867. #endif
  868. /******************/
  869. /* Add References */
  870. /******************/
  871. static UA_StatusCode
  872. deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
  873. const UA_DeleteReferencesItem *item);
  874. /* Adds a one-way reference to the local nodestore */
  875. static UA_StatusCode
  876. addOneWayReference(UA_Server *server, UA_Session *session,
  877. UA_Node *node, const UA_AddReferencesItem *item) {
  878. size_t i = node->referencesSize;
  879. size_t refssize = (i+1) | 3; // so the realloc is not necessary every time
  880. UA_ReferenceNode *new_refs = (UA_ReferenceNode *)UA_realloc(node->references, sizeof(UA_ReferenceNode) * refssize);
  881. if(!new_refs)
  882. return UA_STATUSCODE_BADOUTOFMEMORY;
  883. node->references = new_refs;
  884. UA_ReferenceNode_init(&new_refs[i]);
  885. UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &new_refs[i].referenceTypeId);
  886. retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[i].targetId);
  887. new_refs[i].isInverse = !item->isForward;
  888. if(retval == UA_STATUSCODE_GOOD)
  889. node->referencesSize = i+1;
  890. else
  891. UA_ReferenceNode_deleteMembers(&new_refs[i]);
  892. return retval;
  893. }
  894. UA_StatusCode
  895. Service_AddReferences_single(UA_Server *server, UA_Session *session,
  896. const UA_AddReferencesItem *item) {
  897. /* Currently no expandednodeids are allowed */
  898. if(item->targetServerUri.length > 0)
  899. return UA_STATUSCODE_BADNOTIMPLEMENTED;
  900. /* Add the first direction */
  901. #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
  902. UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
  903. (UA_EditNodeCallback)addOneWayReference,
  904. item);
  905. #else
  906. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  907. UA_Boolean handledExternally = UA_FALSE;
  908. for(size_t j = 0; j <server->externalNamespacesSize; ++j) {
  909. if(item->sourceNodeId.namespaceIndex != server->externalNamespaces[j].index) {
  910. continue;
  911. } else {
  912. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  913. retval = ens->addOneWayReference(ens->ensHandle, item);
  914. handledExternally = UA_TRUE;
  915. break;
  916. }
  917. }
  918. if(!handledExternally)
  919. retval = UA_Server_editNode(server, session, &item->sourceNodeId,
  920. (UA_EditNodeCallback)addOneWayReference, item);
  921. #endif
  922. if(retval != UA_STATUSCODE_GOOD)
  923. return retval;
  924. /* Add the second direction */
  925. UA_AddReferencesItem secondItem;
  926. UA_AddReferencesItem_init(&secondItem);
  927. secondItem.sourceNodeId = item->targetNodeId.nodeId;
  928. secondItem.referenceTypeId = item->referenceTypeId;
  929. secondItem.isForward = !item->isForward;
  930. secondItem.targetNodeId.nodeId = item->sourceNodeId;
  931. /* keep default secondItem.targetNodeClass = UA_NODECLASS_UNSPECIFIED */
  932. #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
  933. retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
  934. (UA_EditNodeCallback)addOneWayReference, &secondItem);
  935. #else
  936. handledExternally = UA_FALSE;
  937. for(size_t j = 0; j < server->externalNamespacesSize; ++j) {
  938. if(secondItem.sourceNodeId.namespaceIndex != server->externalNamespaces[j].index) {
  939. continue;
  940. } else {
  941. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  942. retval = (UA_StatusCode)ens->addOneWayReference(ens->ensHandle, &secondItem);
  943. handledExternally = UA_TRUE;
  944. break;
  945. }
  946. }
  947. if(!handledExternally)
  948. retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
  949. (UA_EditNodeCallback)addOneWayReference, &secondItem);
  950. #endif
  951. /* remove reference if the second direction failed */
  952. if(retval != UA_STATUSCODE_GOOD) {
  953. UA_DeleteReferencesItem deleteItem;
  954. deleteItem.sourceNodeId = item->sourceNodeId;
  955. deleteItem.referenceTypeId = item->referenceTypeId;
  956. deleteItem.isForward = item->isForward;
  957. deleteItem.targetNodeId = item->targetNodeId;
  958. deleteItem.deleteBidirectional = false;
  959. /* ignore returned status code */
  960. UA_Server_editNode(server, session, &item->sourceNodeId,
  961. (UA_EditNodeCallback)deleteOneWayReference, &deleteItem);
  962. }
  963. return retval;
  964. }
  965. void Service_AddReferences(UA_Server *server, UA_Session *session,
  966. const UA_AddReferencesRequest *request,
  967. UA_AddReferencesResponse *response) {
  968. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  969. "Processing AddReferencesRequest");
  970. if(request->referencesToAddSize <= 0) {
  971. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  972. return;
  973. }
  974. response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->referencesToAddSize);
  975. if(!response->results) {
  976. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  977. return;
  978. }
  979. response->resultsSize = request->referencesToAddSize;
  980. #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
  981. for(size_t i = 0; i < response->resultsSize; ++i)
  982. response->results[i] =
  983. Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
  984. #else
  985. size_t size = request->referencesToAddSize;
  986. # ifdef NO_ALLOCA
  987. UA_Boolean isExternal[size];
  988. UA_UInt32 indices[size];
  989. # else
  990. UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
  991. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
  992. # endif /*NO_ALLOCA */
  993. memset(isExternal, false, sizeof(UA_Boolean) * size);
  994. for(size_t j = 0; j < server->externalNamespacesSize; ++j) {
  995. size_t indicesSize = 0;
  996. for(size_t i = 0;i < size;++i) {
  997. if(request->referencesToAdd[i].sourceNodeId.namespaceIndex
  998. != server->externalNamespaces[j].index)
  999. continue;
  1000. isExternal[i] = true;
  1001. indices[indicesSize] = (UA_UInt32)i;
  1002. ++indicesSize;
  1003. }
  1004. if (indicesSize == 0)
  1005. continue;
  1006. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  1007. ens->addReferences(ens->ensHandle, &request->requestHeader, request->referencesToAdd,
  1008. indices, (UA_UInt32)indicesSize, response->results, response->diagnosticInfos);
  1009. }
  1010. for(size_t i = 0; i < response->resultsSize; ++i) {
  1011. if(!isExternal[i])
  1012. response->results[i] =
  1013. Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
  1014. }
  1015. #endif
  1016. }
  1017. UA_StatusCode
  1018. UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
  1019. const UA_NodeId refTypeId, const UA_ExpandedNodeId targetId,
  1020. UA_Boolean isForward) {
  1021. UA_AddReferencesItem item;
  1022. UA_AddReferencesItem_init(&item);
  1023. item.sourceNodeId = sourceId;
  1024. item.referenceTypeId = refTypeId;
  1025. item.isForward = isForward;
  1026. item.targetNodeId = targetId;
  1027. UA_RCU_LOCK();
  1028. UA_StatusCode retval = Service_AddReferences_single(server, &adminSession, &item);
  1029. UA_RCU_UNLOCK();
  1030. return retval;
  1031. }
  1032. /****************/
  1033. /* Delete Nodes */
  1034. /****************/
  1035. static void
  1036. removeReferences(UA_Server *server, UA_Session *session, const UA_Node *node) {
  1037. UA_DeleteReferencesItem item;
  1038. UA_DeleteReferencesItem_init(&item);
  1039. item.targetNodeId.nodeId = node->nodeId;
  1040. for(size_t i = 0; i < node->referencesSize; ++i) {
  1041. item.isForward = node->references[i].isInverse;
  1042. item.sourceNodeId = node->references[i].targetId.nodeId;
  1043. item.referenceTypeId = node->references[i].referenceTypeId;
  1044. Service_DeleteReferences_single(server, session, &item);
  1045. }
  1046. }
  1047. UA_StatusCode
  1048. Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
  1049. const UA_NodeId *nodeId, UA_Boolean deleteReferences) {
  1050. const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
  1051. if(!node)
  1052. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  1053. /* TODO: check if the information model consistency is violated */
  1054. /* Destroy an object before removing it */
  1055. if(node->nodeClass == UA_NODECLASS_OBJECT) {
  1056. /* Call the destructor from the object type */
  1057. const UA_ObjectTypeNode *typenode = getObjectNodeType(server, (const UA_ObjectNode*)node);
  1058. if(typenode && typenode->lifecycleManagement.destructor)
  1059. typenode->lifecycleManagement.destructor(*nodeId, ((const UA_ObjectNode*)node)->instanceHandle);
  1060. }
  1061. /* Remove references to the node (not the references in the node that will
  1062. * be deleted anyway) */
  1063. if(deleteReferences)
  1064. removeReferences(server, session, node);
  1065. return UA_NodeStore_remove(server->nodestore, nodeId);
  1066. }
  1067. void Service_DeleteNodes(UA_Server *server, UA_Session *session,
  1068. const UA_DeleteNodesRequest *request,
  1069. UA_DeleteNodesResponse *response) {
  1070. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  1071. "Processing DeleteNodesRequest");
  1072. if(request->nodesToDeleteSize == 0) {
  1073. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  1074. return;
  1075. }
  1076. response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->nodesToDeleteSize);
  1077. if(!response->results) {
  1078. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;;
  1079. return;
  1080. }
  1081. response->resultsSize = request->nodesToDeleteSize;
  1082. for(size_t i = 0; i < request->nodesToDeleteSize; ++i) {
  1083. UA_DeleteNodesItem *item = &request->nodesToDelete[i];
  1084. response->results[i] = Service_DeleteNodes_single(server, session, &item->nodeId,
  1085. item->deleteTargetReferences);
  1086. }
  1087. }
  1088. UA_StatusCode
  1089. UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId,
  1090. UA_Boolean deleteReferences) {
  1091. UA_RCU_LOCK();
  1092. UA_StatusCode retval = Service_DeleteNodes_single(server, &adminSession,
  1093. &nodeId, deleteReferences);
  1094. UA_RCU_UNLOCK();
  1095. return retval;
  1096. }
  1097. /*********************/
  1098. /* Delete References */
  1099. /*********************/
  1100. // TODO: Check consistency constraints, remove the references.
  1101. static UA_StatusCode
  1102. deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
  1103. const UA_DeleteReferencesItem *item) {
  1104. UA_Boolean edited = false;
  1105. for(size_t i = node->referencesSize; i > 0; --i) {
  1106. UA_ReferenceNode *ref = &node->references[i-1];
  1107. if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &ref->targetId.nodeId))
  1108. continue;
  1109. if(!UA_NodeId_equal(&item->referenceTypeId, &ref->referenceTypeId))
  1110. continue;
  1111. if(item->isForward == ref->isInverse)
  1112. continue;
  1113. UA_ReferenceNode_deleteMembers(ref);
  1114. /* move the last entry to override the current position */
  1115. node->references[i-1] = node->references[node->referencesSize-1];
  1116. --node->referencesSize;
  1117. edited = true;
  1118. break;
  1119. }
  1120. if(!edited)
  1121. return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
  1122. /* we removed the last reference */
  1123. if(node->referencesSize == 0 && node->references) {
  1124. UA_free(node->references);
  1125. node->references = NULL;
  1126. }
  1127. return UA_STATUSCODE_GOOD;;
  1128. }
  1129. UA_StatusCode
  1130. Service_DeleteReferences_single(UA_Server *server, UA_Session *session,
  1131. const UA_DeleteReferencesItem *item) {
  1132. UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
  1133. (UA_EditNodeCallback)deleteOneWayReference, item);
  1134. if(retval != UA_STATUSCODE_GOOD)
  1135. return retval;
  1136. if(!item->deleteBidirectional || item->targetNodeId.serverIndex != 0)
  1137. return retval;
  1138. UA_DeleteReferencesItem secondItem;
  1139. UA_DeleteReferencesItem_init(&secondItem);
  1140. secondItem.isForward = !item->isForward;
  1141. secondItem.sourceNodeId = item->targetNodeId.nodeId;
  1142. secondItem.targetNodeId.nodeId = item->sourceNodeId;
  1143. secondItem.referenceTypeId = item->referenceTypeId;
  1144. return UA_Server_editNode(server, session, &secondItem.sourceNodeId,
  1145. (UA_EditNodeCallback)deleteOneWayReference, &secondItem);
  1146. }
  1147. void
  1148. Service_DeleteReferences(UA_Server *server, UA_Session *session,
  1149. const UA_DeleteReferencesRequest *request,
  1150. UA_DeleteReferencesResponse *response) {
  1151. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  1152. "Processing DeleteReferencesRequest");
  1153. if(request->referencesToDeleteSize <= 0) {
  1154. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  1155. return;
  1156. }
  1157. response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->referencesToDeleteSize);
  1158. if(!response->results) {
  1159. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;;
  1160. return;
  1161. }
  1162. response->resultsSize = request->referencesToDeleteSize;
  1163. for(size_t i = 0; i < request->referencesToDeleteSize; ++i)
  1164. response->results[i] =
  1165. Service_DeleteReferences_single(server, session, &request->referencesToDelete[i]);
  1166. }
  1167. UA_StatusCode
  1168. UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
  1169. const UA_NodeId referenceTypeId,
  1170. UA_Boolean isForward, const UA_ExpandedNodeId targetNodeId,
  1171. UA_Boolean deleteBidirectional) {
  1172. UA_DeleteReferencesItem item;
  1173. item.sourceNodeId = sourceNodeId;
  1174. item.referenceTypeId = referenceTypeId;
  1175. item.isForward = isForward;
  1176. item.targetNodeId = targetNodeId;
  1177. item.deleteBidirectional = deleteBidirectional;
  1178. UA_RCU_LOCK();
  1179. UA_StatusCode retval = Service_DeleteReferences_single(server, &adminSession, &item);
  1180. UA_RCU_UNLOCK();
  1181. return retval;
  1182. }
  1183. /**********************/
  1184. /* Set Value Callback */
  1185. /**********************/
  1186. static UA_StatusCode
  1187. setValueCallback(UA_Server *server, UA_Session *session,
  1188. UA_VariableNode *node, UA_ValueCallback *callback) {
  1189. if(node->nodeClass != UA_NODECLASS_VARIABLE)
  1190. return UA_STATUSCODE_BADNODECLASSINVALID;
  1191. node->value.data.callback = *callback;
  1192. return UA_STATUSCODE_GOOD;
  1193. }
  1194. UA_StatusCode UA_EXPORT
  1195. UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeId,
  1196. const UA_ValueCallback callback) {
  1197. UA_RCU_LOCK();
  1198. UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
  1199. (UA_EditNodeCallback)setValueCallback, &callback);
  1200. UA_RCU_UNLOCK();
  1201. return retval;
  1202. }
  1203. /******************/
  1204. /* Set DataSource */
  1205. /******************/
  1206. static UA_StatusCode
  1207. setDataSource(UA_Server *server, UA_Session *session,
  1208. UA_VariableNode* node, UA_DataSource *dataSource) {
  1209. if(node->nodeClass != UA_NODECLASS_VARIABLE)
  1210. return UA_STATUSCODE_BADNODECLASSINVALID;
  1211. if(node->valueSource == UA_VALUESOURCE_DATA)
  1212. UA_DataValue_deleteMembers(&node->value.data.value);
  1213. node->value.dataSource = *dataSource;
  1214. node->valueSource = UA_VALUESOURCE_DATASOURCE;
  1215. return UA_STATUSCODE_GOOD;
  1216. }
  1217. UA_StatusCode
  1218. UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
  1219. const UA_DataSource dataSource) {
  1220. UA_RCU_LOCK();
  1221. UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
  1222. (UA_EditNodeCallback)setDataSource, &dataSource);
  1223. UA_RCU_UNLOCK();
  1224. return retval;
  1225. }
  1226. /****************************/
  1227. /* Set Lifecycle Management */
  1228. /****************************/
  1229. static UA_StatusCode
  1230. setOLM(UA_Server *server, UA_Session *session,
  1231. UA_ObjectTypeNode* node, UA_ObjectLifecycleManagement *olm) {
  1232. if(node->nodeClass != UA_NODECLASS_OBJECTTYPE)
  1233. return UA_STATUSCODE_BADNODECLASSINVALID;
  1234. node->lifecycleManagement = *olm;
  1235. return UA_STATUSCODE_GOOD;
  1236. }
  1237. UA_StatusCode UA_EXPORT
  1238. UA_Server_setObjectTypeNode_lifecycleManagement(UA_Server *server, UA_NodeId nodeId,
  1239. UA_ObjectLifecycleManagement olm) {
  1240. UA_RCU_LOCK();
  1241. UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
  1242. (UA_EditNodeCallback)setOLM, &olm);
  1243. UA_RCU_UNLOCK();
  1244. return retval;
  1245. }
  1246. /***********************/
  1247. /* Set Method Callback */
  1248. /***********************/
  1249. #ifdef UA_ENABLE_METHODCALLS
  1250. struct addMethodCallback {
  1251. UA_MethodCallback callback;
  1252. void *handle;
  1253. };
  1254. static UA_StatusCode
  1255. editMethodCallback(UA_Server *server, UA_Session* session,
  1256. UA_Node* node, const void* handle) {
  1257. if(node->nodeClass != UA_NODECLASS_METHOD)
  1258. return UA_STATUSCODE_BADNODECLASSINVALID;
  1259. const struct addMethodCallback *newCallback = (const struct addMethodCallback *)handle;
  1260. UA_MethodNode *mnode = (UA_MethodNode*) node;
  1261. mnode->attachedMethod = newCallback->callback;
  1262. mnode->methodHandle = newCallback->handle;
  1263. return UA_STATUSCODE_GOOD;
  1264. }
  1265. UA_StatusCode UA_EXPORT
  1266. UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId,
  1267. UA_MethodCallback method, void *handle) {
  1268. struct addMethodCallback cb = { method, handle };
  1269. UA_RCU_LOCK();
  1270. UA_StatusCode retval = UA_Server_editNode(server, &adminSession,
  1271. &methodNodeId, editMethodCallback, &cb);
  1272. UA_RCU_UNLOCK();
  1273. return retval;
  1274. }
  1275. #endif