ua_services_attribute.c 22 KB

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