ua_services_nodemanagement.c 59 KB

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