ua_services_attribute.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. #include "ua_server_internal.h"
  2. #include "ua_types_generated.h"
  3. #include "ua_services.h"
  4. #include "ua_statuscodes.h"
  5. #include "ua_nodestore.h"
  6. #include "ua_util.h"
  7. #define CHECK_NODECLASS(CLASS) \
  8. if(!(node->nodeClass & (CLASS))) { \
  9. v->hasStatus = UA_TRUE; \
  10. v->status = UA_STATUSCODE_BADATTRIBUTEIDINVALID; \
  11. break; \
  12. }
  13. static void handleServerTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
  14. if (v && (timestamps == UA_TIMESTAMPSTORETURN_SERVER
  15. || timestamps == UA_TIMESTAMPSTORETURN_BOTH)) {
  16. v->hasServerTimestamp = UA_TRUE;
  17. v->serverTimestamp = UA_DateTime_now();
  18. }
  19. }
  20. static void handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
  21. if(timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
  22. v->hasSourceTimestamp = UA_TRUE;
  23. v->sourceTimestamp = UA_DateTime_now();
  24. }
  25. }
  26. /** Reads a single attribute from a node in the nodestore. */
  27. static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
  28. const UA_ReadValueId *id, UA_DataValue *v) {
  29. UA_Node const *node = UA_NodeStore_get(server->nodestore, &(id->nodeId));
  30. if(!node) {
  31. v->hasStatus = UA_TRUE;
  32. v->status = UA_STATUSCODE_BADNODEIDUNKNOWN;
  33. return;
  34. }
  35. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  36. switch(id->attributeId) {
  37. case UA_ATTRIBUTEID_NODEID:
  38. v->hasVariant = UA_TRUE;
  39. retval |= UA_Variant_copySetValue(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
  40. break;
  41. case UA_ATTRIBUTEID_NODECLASS:
  42. v->hasVariant = UA_TRUE;
  43. retval |= UA_Variant_copySetValue(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
  44. break;
  45. case UA_ATTRIBUTEID_BROWSENAME:
  46. v->hasVariant = UA_TRUE;
  47. retval |= UA_Variant_copySetValue(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
  48. break;
  49. case UA_ATTRIBUTEID_DISPLAYNAME:
  50. retval |= UA_Variant_copySetValue(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  51. if(retval == UA_STATUSCODE_GOOD)
  52. v->hasVariant = UA_TRUE;
  53. break;
  54. case UA_ATTRIBUTEID_DESCRIPTION:
  55. v->hasVariant = UA_TRUE;
  56. retval |= UA_Variant_copySetValue(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  57. break;
  58. case UA_ATTRIBUTEID_WRITEMASK:
  59. v->hasVariant = UA_TRUE;
  60. retval |= UA_Variant_copySetValue(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
  61. break;
  62. case UA_ATTRIBUTEID_USERWRITEMASK:
  63. v->hasVariant = UA_TRUE;
  64. retval |= UA_Variant_copySetValue(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
  65. break;
  66. case UA_ATTRIBUTEID_ISABSTRACT:
  67. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE |
  68. UA_NODECLASS_DATATYPE);
  69. v->hasVariant = UA_TRUE;
  70. retval |= UA_Variant_copySetValue(&v->value, &((const UA_ReferenceTypeNode *)node)->isAbstract,
  71. &UA_TYPES[UA_TYPES_BOOLEAN]);
  72. break;
  73. case UA_ATTRIBUTEID_SYMMETRIC:
  74. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
  75. v->hasVariant = UA_TRUE;
  76. retval |= UA_Variant_copySetValue(&v->value, &((const UA_ReferenceTypeNode *)node)->symmetric,
  77. &UA_TYPES[UA_TYPES_BOOLEAN]);
  78. break;
  79. case UA_ATTRIBUTEID_INVERSENAME:
  80. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
  81. v->hasVariant = UA_TRUE;
  82. retval |= UA_Variant_copySetValue(&v->value, &((const UA_ReferenceTypeNode *)node)->inverseName,
  83. &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  84. break;
  85. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  86. CHECK_NODECLASS(UA_NODECLASS_VIEW);
  87. v->hasVariant = UA_TRUE;
  88. retval |= UA_Variant_copySetValue(&v->value, &((const UA_ViewNode *)node)->containsNoLoops,
  89. &UA_TYPES[UA_TYPES_BOOLEAN]);
  90. break;
  91. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  92. CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
  93. v->hasVariant = UA_TRUE;
  94. if(node->nodeClass == UA_NODECLASS_VIEW){
  95. retval |= UA_Variant_copySetValue(&v->value, &((const UA_ViewNode *)node)->eventNotifier,
  96. &UA_TYPES[UA_TYPES_BYTE]);
  97. } else {
  98. retval |= UA_Variant_copySetValue(&v->value, &((const UA_ObjectNode *)node)->eventNotifier,
  99. &UA_TYPES[UA_TYPES_BYTE]);
  100. }
  101. break;
  102. case UA_ATTRIBUTEID_VALUE:
  103. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  104. {
  105. if(node->nodeClass == UA_NODECLASS_VARIABLE){
  106. const UA_VariableNode *vn = (const UA_VariableNode*)node;
  107. if(vn->variableType == UA_VARIABLENODETYPE_VARIANT) {
  108. retval = UA_Variant_copy(&vn->variable.variant, &v->value);
  109. if(retval != UA_STATUSCODE_GOOD)
  110. break;
  111. v->hasVariant = UA_TRUE;
  112. handleSourceTimestamps(timestamps, v);
  113. } else {
  114. UA_DataValue val;
  115. UA_DataValue_init(&val);
  116. UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
  117. timestamps == UA_TIMESTAMPSTORETURN_BOTH);
  118. retval |= vn->variable.dataSource.read(vn->variable.dataSource.handle, sourceTimeStamp, &val);
  119. if(retval != UA_STATUSCODE_GOOD)
  120. break;
  121. retval |= UA_DataValue_copy(&val, v);
  122. vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
  123. if(retval != UA_STATUSCODE_GOOD)
  124. break;
  125. }
  126. }else{
  127. v->hasVariant = UA_FALSE;
  128. handleSourceTimestamps(timestamps, v);
  129. }
  130. }
  131. break;
  132. case UA_ATTRIBUTEID_DATATYPE:
  133. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  134. v->hasVariant = UA_TRUE;
  135. if(node->nodeClass == UA_NODECLASS_VARIABLETYPE){
  136. retval |= UA_Variant_copySetValue(&v->value,
  137. &((const UA_VariableTypeNode *)node)->value.type->typeId,
  138. &UA_TYPES[UA_TYPES_NODEID]);
  139. } else {
  140. const UA_VariableNode *vn = (const UA_VariableNode*)node;
  141. if(vn->variableType == UA_VARIABLENODETYPE_VARIANT)
  142. retval |= UA_Variant_copySetValue(&v->value, &vn->variable.variant.type->typeId,
  143. &UA_TYPES[UA_TYPES_NODEID]);
  144. else {
  145. UA_DataValue val;
  146. UA_DataValue_init(&val);
  147. retval |= vn->variable.dataSource.read(vn->variable.dataSource.handle, UA_FALSE, &val);
  148. if(retval != UA_STATUSCODE_GOOD)
  149. break;
  150. retval |= UA_Variant_copySetValue(&v->value, &val.value.type->typeId,
  151. &UA_TYPES[UA_TYPES_NODEID]);
  152. vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
  153. if(retval != UA_STATUSCODE_GOOD)
  154. break;
  155. }
  156. }
  157. break;
  158. case UA_ATTRIBUTEID_VALUERANK:
  159. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  160. v->hasVariant = UA_TRUE;
  161. retval |= UA_Variant_copySetValue(&v->value, &((const UA_VariableTypeNode *)node)->valueRank,
  162. &UA_TYPES[UA_TYPES_INT32]);
  163. break;
  164. case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
  165. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  166. {
  167. if(node->nodeClass == UA_NODECLASS_VARIABLE){
  168. const UA_VariableNode *vn = (const UA_VariableNode *)node;
  169. if(vn->variableType == UA_VARIABLENODETYPE_VARIANT) {
  170. retval = UA_Variant_copySetArray(&v->value, vn->variable.variant.arrayDimensions,
  171. vn->variable.variant.arrayDimensionsSize,
  172. &UA_TYPES[UA_TYPES_INT32]);
  173. if(retval == UA_STATUSCODE_GOOD)
  174. v->hasVariant = UA_TRUE;
  175. } else {
  176. UA_DataValue val;
  177. UA_DataValue_init(&val);
  178. retval |= vn->variable.dataSource.read(vn->variable.dataSource.handle, UA_FALSE, &val);
  179. if(retval != UA_STATUSCODE_GOOD)
  180. break;
  181. if(!val.hasVariant) {
  182. vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
  183. retval = UA_STATUSCODE_BADNOTREADABLE;
  184. break;
  185. }
  186. retval = UA_Variant_copySetArray(&v->value, val.value.arrayDimensions,
  187. val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
  188. vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
  189. }
  190. }else{
  191. retval = UA_STATUSCODE_GOOD;
  192. }
  193. }
  194. break;
  195. case UA_ATTRIBUTEID_ACCESSLEVEL:
  196. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  197. v->hasVariant = UA_TRUE;
  198. retval |= UA_Variant_copySetValue(&v->value, &((const UA_VariableNode *)node)->accessLevel,
  199. &UA_TYPES[UA_TYPES_BYTE]);
  200. break;
  201. case UA_ATTRIBUTEID_USERACCESSLEVEL:
  202. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  203. v->hasVariant = UA_TRUE;
  204. retval |= UA_Variant_copySetValue(&v->value, &((const UA_VariableNode *)node)->userAccessLevel,
  205. &UA_TYPES[UA_TYPES_BYTE]);
  206. break;
  207. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  208. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  209. v->hasVariant = UA_TRUE;
  210. retval |= UA_Variant_copySetValue(&v->value, &((const UA_VariableNode *)node)->minimumSamplingInterval,
  211. &UA_TYPES[UA_TYPES_DOUBLE]);
  212. break;
  213. case UA_ATTRIBUTEID_HISTORIZING:
  214. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  215. v->hasVariant = UA_TRUE;
  216. retval |= UA_Variant_copySetValue(&v->value, &((const UA_VariableNode *)node)->historizing,
  217. &UA_TYPES[UA_TYPES_BOOLEAN]);
  218. break;
  219. case UA_ATTRIBUTEID_EXECUTABLE:
  220. CHECK_NODECLASS(UA_NODECLASS_METHOD);
  221. v->hasVariant = UA_TRUE;
  222. retval |= UA_Variant_copySetValue(&v->value, &((const UA_MethodNode *)node)->executable,
  223. &UA_TYPES[UA_TYPES_BOOLEAN]);
  224. break;
  225. case UA_ATTRIBUTEID_USEREXECUTABLE:
  226. CHECK_NODECLASS(UA_NODECLASS_METHOD);
  227. v->hasVariant = UA_TRUE;
  228. retval |= UA_Variant_copySetValue(&v->value, &((const UA_MethodNode *)node)->userExecutable,
  229. &UA_TYPES[UA_TYPES_BOOLEAN]);
  230. break;
  231. default:
  232. v->hasStatus = UA_TRUE;
  233. v->status = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  234. break;
  235. }
  236. UA_NodeStore_release(node);
  237. if(retval != UA_STATUSCODE_GOOD) {
  238. v->hasStatus = UA_TRUE;
  239. v->status = UA_STATUSCODE_BADNOTREADABLE;
  240. }
  241. handleServerTimestamps(timestamps, v);
  242. }
  243. void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
  244. UA_ReadResponse *response) {
  245. if(request->nodesToReadSize <= 0) {
  246. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  247. return;
  248. }
  249. size_t size = request->nodesToReadSize;
  250. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_DATAVALUE], size);
  251. if(!response->results) {
  252. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  253. return;
  254. }
  255. response->resultsSize = size;
  256. if(request->maxAge < 0) {
  257. response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID;
  258. return;
  259. }
  260. /* ### Begin External Namespaces */
  261. UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
  262. UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
  263. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
  264. for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
  265. size_t indexSize = 0;
  266. for(size_t i = 0;i < size;i++) {
  267. if(request->nodesToRead[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
  268. continue;
  269. isExternal[i] = UA_TRUE;
  270. indices[indexSize] = i;
  271. indexSize++;
  272. }
  273. if(indexSize == 0)
  274. continue;
  275. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  276. ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
  277. indices, indexSize, response->results, UA_FALSE, response->diagnosticInfos);
  278. }
  279. /* ### End External Namespaces */
  280. for(size_t i = 0;i < size;i++) {
  281. if(!isExternal[i])
  282. readValue(server, request->timestampsToReturn, &request->nodesToRead[i], &response->results[i]);
  283. }
  284. #ifdef EXTENSION_STATELESS
  285. if(session==&anonymousSession){
  286. /* expiry header */
  287. UA_ExtensionObject additionalHeader;
  288. UA_ExtensionObject_init(&additionalHeader);
  289. additionalHeader.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
  290. UA_Variant variant;
  291. UA_Variant_init(&variant);
  292. variant.type = &UA_TYPES[UA_TYPES_DATETIME];
  293. variant.arrayLength = request->nodesToReadSize;
  294. UA_DateTime* expireArray = UA_NULL;
  295. expireArray = UA_Array_new(&UA_TYPES[UA_TYPES_DATETIME], request->nodesToReadSize);
  296. variant.dataPtr = expireArray;
  297. UA_ByteString str;
  298. UA_ByteString_init(&str);
  299. /*expires in 20 seconds*/
  300. for(UA_Int32 i = 0;i < response->resultsSize;i++) {
  301. expireArray[i] = UA_DateTime_now() + 20 * 100 * 1000 * 1000;
  302. }
  303. size_t offset = 0;
  304. str.data = UA_malloc(UA_Variant_calcSizeBinary(&variant));
  305. str.length = UA_Variant_calcSizeBinary(&variant);
  306. UA_Variant_encodeBinary(&variant, &str, &offset);
  307. additionalHeader.body = str;
  308. response->responseHeader.additionalHeader = additionalHeader;
  309. }
  310. #endif
  311. }
  312. static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
  313. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  314. // we might repeat writing, e.g. when the node got replaced mid-work
  315. UA_Boolean done = UA_FALSE;
  316. while(!done) {
  317. const UA_Node *node = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
  318. if(!node)
  319. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  320. switch(wvalue->attributeId) {
  321. case UA_ATTRIBUTEID_NODEID:
  322. case UA_ATTRIBUTEID_NODECLASS:
  323. case UA_ATTRIBUTEID_BROWSENAME:
  324. case UA_ATTRIBUTEID_DISPLAYNAME:
  325. case UA_ATTRIBUTEID_DESCRIPTION:
  326. case UA_ATTRIBUTEID_WRITEMASK:
  327. case UA_ATTRIBUTEID_USERWRITEMASK:
  328. case UA_ATTRIBUTEID_ISABSTRACT:
  329. case UA_ATTRIBUTEID_SYMMETRIC:
  330. case UA_ATTRIBUTEID_INVERSENAME:
  331. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  332. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  333. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  334. break;
  335. case UA_ATTRIBUTEID_VALUE:
  336. if(node->nodeClass == UA_NODECLASS_VARIABLE) {
  337. const UA_VariableNode *vn = (const UA_VariableNode*)node;
  338. if(vn->variableType == UA_VARIABLENODETYPE_DATASOURCE) {
  339. if(!vn->variable.dataSource.write) {
  340. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  341. break;
  342. }
  343. retval = vn->variable.dataSource.write(vn->variable.dataSource.handle, &wvalue->value.value);
  344. done = UA_TRUE;
  345. break;
  346. }
  347. // array sizes are not compared
  348. if(!wvalue->value.hasVariant) {
  349. retval = UA_STATUSCODE_BADTYPEMISMATCH;
  350. break;
  351. }
  352. if(!UA_NodeId_equal(&vn->variable.variant.type->typeId, &wvalue->value.value.type->typeId)) {
  353. // when the nodeids differ, it is possible that an enum was sent as an int, or
  354. // an opaque type as a bytestring
  355. if(!vn->variable.variant.type->namespaceZero || wvalue->value.value.type->namespaceZero ||
  356. vn->variable.variant.type->typeIndex != wvalue->value.value.type->typeIndex) {
  357. retval = UA_STATUSCODE_BADTYPEMISMATCH;
  358. break;
  359. }
  360. wvalue->value.value.type = vn->variable.variant.type;
  361. }
  362. UA_VariableNode *newVn = UA_VariableNode_new();
  363. if(!newVn) {
  364. retval = UA_STATUSCODE_BADOUTOFMEMORY;
  365. break;
  366. }
  367. retval = UA_VariableNode_copy(vn, newVn);
  368. if(retval != UA_STATUSCODE_GOOD) {
  369. UA_VariableNode_delete(newVn);
  370. break;
  371. }
  372. retval = UA_Variant_copy(&wvalue->value.value, &newVn->variable.variant);
  373. if(retval != UA_STATUSCODE_GOOD) {
  374. UA_VariableNode_delete(newVn);
  375. break;
  376. }
  377. if(UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn,
  378. UA_NULL) == UA_STATUSCODE_GOOD)
  379. done = UA_TRUE;
  380. else
  381. UA_VariableNode_delete(newVn);
  382. } else if(node->nodeClass == UA_NODECLASS_VARIABLETYPE) {
  383. const UA_VariableTypeNode *vtn = (const UA_VariableTypeNode*)node;
  384. if(!wvalue->value.hasVariant) {
  385. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  386. break;
  387. }
  388. if(!UA_NodeId_equal(&vtn->value.type->typeId, &wvalue->value.value.type->typeId)) {
  389. if(!vtn->value.type->namespaceZero || wvalue->value.value.type->namespaceZero ||
  390. vtn->value.type->typeIndex != wvalue->value.value.type->typeIndex) {
  391. retval = UA_STATUSCODE_BADTYPEMISMATCH;
  392. break;
  393. }
  394. wvalue->value.value.type = vtn->value.type;
  395. }
  396. UA_VariableTypeNode *newVtn = UA_VariableTypeNode_new();
  397. if(!newVtn) {
  398. retval = UA_STATUSCODE_BADOUTOFMEMORY;
  399. break;
  400. }
  401. retval = UA_VariableTypeNode_copy(vtn, newVtn);
  402. if(retval != UA_STATUSCODE_GOOD) {
  403. UA_VariableTypeNode_delete(newVtn);
  404. break;
  405. }
  406. retval = UA_Variant_copy(&wvalue->value.value, &newVtn->value);
  407. if(retval != UA_STATUSCODE_GOOD) {
  408. UA_VariableTypeNode_delete(newVtn);
  409. break;
  410. }
  411. if(UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVtn,
  412. UA_NULL) == UA_STATUSCODE_GOOD)
  413. done = UA_TRUE;
  414. else
  415. UA_VariableTypeNode_delete(newVtn);
  416. } else {
  417. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  418. }
  419. break;
  420. case UA_ATTRIBUTEID_DATATYPE:
  421. case UA_ATTRIBUTEID_VALUERANK:
  422. case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
  423. case UA_ATTRIBUTEID_ACCESSLEVEL:
  424. case UA_ATTRIBUTEID_USERACCESSLEVEL:
  425. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  426. case UA_ATTRIBUTEID_HISTORIZING:
  427. case UA_ATTRIBUTEID_EXECUTABLE:
  428. case UA_ATTRIBUTEID_USEREXECUTABLE:
  429. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  430. break;
  431. default:
  432. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  433. break;
  434. }
  435. if(retval != UA_STATUSCODE_GOOD)
  436. break;
  437. UA_NodeStore_release(node);
  438. }
  439. return retval;
  440. }
  441. void Service_Write(UA_Server *server, UA_Session *session,
  442. const UA_WriteRequest *request, UA_WriteResponse *response) {
  443. UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
  444. if(request->nodesToWriteSize <= 0){
  445. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  446. return;
  447. }
  448. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_STATUSCODE], request->nodesToWriteSize);
  449. if(!response->results) {
  450. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  451. return;
  452. }
  453. /* ### Begin External Namespaces */
  454. UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToWriteSize);
  455. UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
  456. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToWriteSize);
  457. for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
  458. UA_UInt32 indexSize = 0;
  459. for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
  460. if(request->nodesToWrite[i].nodeId.namespaceIndex !=
  461. server->externalNamespaces[j].index)
  462. continue;
  463. isExternal[i] = UA_TRUE;
  464. indices[indexSize] = i;
  465. indexSize++;
  466. }
  467. if(indexSize == 0)
  468. continue;
  469. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  470. ens->writeNodes(ens->ensHandle, &request->requestHeader, request->nodesToWrite,
  471. indices, indexSize, response->results, response->diagnosticInfos);
  472. }
  473. /* ### End External Namespaces */
  474. response->resultsSize = request->nodesToWriteSize;
  475. for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
  476. if(!isExternal[i])
  477. response->results[i] = writeValue(server, &request->nodesToWrite[i]);
  478. }
  479. }