ua_services_attribute.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  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. static UA_StatusCode
  8. parse_numericrange(const UA_String str, UA_NumericRange *range)
  9. {
  10. if(str.length < 0 || str.length >= 1023)
  11. return UA_STATUSCODE_BADINTERNALERROR;
  12. char *cstring = UA_alloca(str.length+1);
  13. UA_memcpy(cstring, str.data, str.length);
  14. cstring[str.length] = 0;
  15. UA_Int32 index = 0;
  16. size_t dimensionsIndex = 0;
  17. size_t dimensionsMax = 3; // more should be uncommon
  18. struct UA_NumericRangeDimension *dimensions = UA_malloc(sizeof(struct UA_NumericRangeDimension) * 3);
  19. if(!dimensions)
  20. return UA_STATUSCODE_BADOUTOFMEMORY;
  21. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  22. do {
  23. UA_Int32 min, max;
  24. UA_Int32 progress;
  25. UA_Int32 res = sscanf(&cstring[index], "%" SCNu32 "%n", &min, &progress);
  26. if(res <= 0 || min < 0) {
  27. retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
  28. break;
  29. }
  30. index += progress;
  31. if(index >= str.length || cstring[index] == ',')
  32. max = min;
  33. else {
  34. res = sscanf(&cstring[index], ":%" SCNu32 "%n", &max, &progress);
  35. if(res <= 0 || max < 0 || min >= max) {
  36. retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
  37. break;
  38. }
  39. index += progress;
  40. }
  41. if(dimensionsIndex >= dimensionsMax) {
  42. struct UA_NumericRangeDimension *newDimensions =
  43. UA_realloc(dimensions, sizeof(struct UA_NumericRangeDimension) * 2 * dimensionsMax);
  44. if(!newDimensions) {
  45. UA_free(dimensions);
  46. return UA_STATUSCODE_BADOUTOFMEMORY;
  47. }
  48. dimensions = newDimensions;
  49. dimensionsMax *= 2;
  50. }
  51. dimensions[dimensionsIndex].min = min;
  52. dimensions[dimensionsIndex].max = max;
  53. dimensionsIndex++;
  54. } while(retval == UA_STATUSCODE_GOOD && index + 1 < str.length && cstring[index] == ',' && ++index);
  55. if(retval != UA_STATUSCODE_GOOD) {
  56. UA_free(dimensions);
  57. return retval;
  58. }
  59. range->dimensions = dimensions;
  60. range->dimensionsSize = dimensionsIndex;
  61. return retval;
  62. }
  63. #define CHECK_NODECLASS(CLASS) \
  64. if(!(node->nodeClass & (CLASS))) { \
  65. v->hasStatus = UA_TRUE; \
  66. v->status = UA_STATUSCODE_BADATTRIBUTEIDINVALID; \
  67. break; \
  68. }
  69. static void handleServerTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
  70. if (v && (timestamps == UA_TIMESTAMPSTORETURN_SERVER
  71. || timestamps == UA_TIMESTAMPSTORETURN_BOTH)) {
  72. v->hasServerTimestamp = UA_TRUE;
  73. v->serverTimestamp = UA_DateTime_now();
  74. }
  75. }
  76. static void handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
  77. if(timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
  78. v->hasSourceTimestamp = UA_TRUE;
  79. v->sourceTimestamp = UA_DateTime_now();
  80. }
  81. }
  82. /** Reads a single attribute from a node in the nodestore. */
  83. static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
  84. const UA_ReadValueId *id, UA_DataValue *v) {
  85. if(id->dataEncoding.name.length >= 0){
  86. if(memcmp(id->dataEncoding.name.data, "DefaultBinary", 13) != 0 &&
  87. memcmp(id->dataEncoding.name.data, "DefaultXml", 10) != 0) {
  88. v->hasStatus = UA_TRUE;
  89. v->status = UA_STATUSCODE_BADDATAENCODINGINVALID;
  90. return;
  91. }
  92. }
  93. //index range for a non-value
  94. if(id->indexRange.length >= 0 && id->attributeId != UA_ATTRIBUTEID_VALUE){
  95. v->hasStatus = UA_TRUE;
  96. v->status = UA_STATUSCODE_BADINDEXRANGENODATA;
  97. return;
  98. }
  99. UA_Node const *node = UA_NodeStore_get(server->nodestore, &(id->nodeId));
  100. if(!node) {
  101. v->hasStatus = UA_TRUE;
  102. v->status = UA_STATUSCODE_BADNODEIDUNKNOWN;
  103. return;
  104. }
  105. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  106. switch(id->attributeId) {
  107. case UA_ATTRIBUTEID_NODEID:
  108. v->hasValue = UA_TRUE;
  109. retval |= UA_Variant_setScalarCopy(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
  110. break;
  111. case UA_ATTRIBUTEID_NODECLASS:
  112. v->hasValue = UA_TRUE;
  113. retval |= UA_Variant_setScalarCopy(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
  114. break;
  115. case UA_ATTRIBUTEID_BROWSENAME:
  116. v->hasValue = UA_TRUE;
  117. retval |= UA_Variant_setScalarCopy(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
  118. break;
  119. case UA_ATTRIBUTEID_DISPLAYNAME:
  120. retval |= UA_Variant_setScalarCopy(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  121. if(retval == UA_STATUSCODE_GOOD)
  122. v->hasValue = UA_TRUE;
  123. break;
  124. case UA_ATTRIBUTEID_DESCRIPTION:
  125. v->hasValue = UA_TRUE;
  126. retval |= UA_Variant_setScalarCopy(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  127. break;
  128. case UA_ATTRIBUTEID_WRITEMASK:
  129. v->hasValue = UA_TRUE;
  130. retval |= UA_Variant_setScalarCopy(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
  131. break;
  132. case UA_ATTRIBUTEID_USERWRITEMASK:
  133. v->hasValue = UA_TRUE;
  134. retval |= UA_Variant_setScalarCopy(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
  135. break;
  136. case UA_ATTRIBUTEID_ISABSTRACT:
  137. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE |
  138. UA_NODECLASS_DATATYPE);
  139. v->hasValue = UA_TRUE;
  140. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->isAbstract,
  141. &UA_TYPES[UA_TYPES_BOOLEAN]);
  142. break;
  143. case UA_ATTRIBUTEID_SYMMETRIC:
  144. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
  145. v->hasValue = UA_TRUE;
  146. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->symmetric,
  147. &UA_TYPES[UA_TYPES_BOOLEAN]);
  148. break;
  149. case UA_ATTRIBUTEID_INVERSENAME:
  150. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
  151. v->hasValue = UA_TRUE;
  152. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->inverseName,
  153. &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  154. break;
  155. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  156. CHECK_NODECLASS(UA_NODECLASS_VIEW);
  157. v->hasValue = UA_TRUE;
  158. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode *)node)->containsNoLoops,
  159. &UA_TYPES[UA_TYPES_BOOLEAN]);
  160. break;
  161. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  162. CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
  163. v->hasValue = UA_TRUE;
  164. if(node->nodeClass == UA_NODECLASS_VIEW){
  165. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode *)node)->eventNotifier,
  166. &UA_TYPES[UA_TYPES_BYTE]);
  167. } else {
  168. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ObjectNode *)node)->eventNotifier,
  169. &UA_TYPES[UA_TYPES_BYTE]);
  170. }
  171. break;
  172. case UA_ATTRIBUTEID_VALUE:
  173. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  174. {
  175. if(node->nodeClass != UA_NODECLASS_VARIABLE) {
  176. v->hasValue = UA_FALSE;
  177. handleSourceTimestamps(timestamps, v);
  178. }
  179. UA_Boolean hasRange = UA_FALSE;
  180. UA_NumericRange range;
  181. if(id->indexRange.length > 0) {
  182. retval = parse_numericrange(id->indexRange, &range);
  183. if(retval != UA_STATUSCODE_GOOD)
  184. break;
  185. hasRange = UA_TRUE;
  186. }
  187. const UA_VariableNode *vn = (const UA_VariableNode*)node;
  188. if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
  189. if(hasRange)
  190. retval |= UA_Variant_copyRange(&vn->value.variant, &v->value, range);
  191. else
  192. retval |= UA_Variant_copy(&vn->value.variant, &v->value);
  193. if(retval == UA_STATUSCODE_GOOD) {
  194. v->hasValue = UA_TRUE;
  195. handleSourceTimestamps(timestamps, v);
  196. }
  197. } else {
  198. UA_DataValue val;
  199. UA_DataValue_init(&val);
  200. UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
  201. timestamps == UA_TIMESTAMPSTORETURN_BOTH);
  202. retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, &val);
  203. if(retval == UA_STATUSCODE_GOOD) {
  204. retval |= UA_DataValue_copy(&val, v); // todo: selection of indexranges
  205. }
  206. vn->value.dataSource.release(vn->value.dataSource.handle, &val);
  207. }
  208. if(hasRange)
  209. UA_free(range.dimensions);
  210. }
  211. break;
  212. case UA_ATTRIBUTEID_DATATYPE: {
  213. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  214. const UA_VariableNode *vn = (const UA_VariableNode*)node;
  215. if(vn->valueSource == UA_VALUESOURCE_VARIANT)
  216. retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.type->typeId,
  217. &UA_TYPES[UA_TYPES_NODEID]);
  218. else {
  219. UA_DataValue val;
  220. UA_DataValue_init(&val);
  221. retval = vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val);
  222. if(retval != UA_STATUSCODE_GOOD)
  223. break;
  224. retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
  225. vn->value.dataSource.release(vn->value.dataSource.handle, &val);
  226. }
  227. if(retval == UA_STATUSCODE_GOOD)
  228. v->hasValue = UA_TRUE;
  229. }
  230. break;
  231. case UA_ATTRIBUTEID_VALUERANK:
  232. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  233. v->hasValue = UA_TRUE;
  234. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode *)node)->valueRank,
  235. &UA_TYPES[UA_TYPES_INT32]);
  236. break;
  237. case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
  238. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  239. {
  240. const UA_VariableNode *vn = (const UA_VariableNode *)node;
  241. if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
  242. retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.arrayDimensions,
  243. vn->value.variant.arrayDimensionsSize,
  244. &UA_TYPES[UA_TYPES_INT32]);
  245. if(retval == UA_STATUSCODE_GOOD)
  246. v->hasValue = UA_TRUE;
  247. } else {
  248. UA_DataValue val;
  249. UA_DataValue_init(&val);
  250. retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val);
  251. if(retval != UA_STATUSCODE_GOOD)
  252. break;
  253. if(!val.hasValue)
  254. retval = UA_STATUSCODE_BADNOTREADABLE;
  255. else
  256. retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions,
  257. val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
  258. vn->value.dataSource.release(vn->value.dataSource.handle, &val);
  259. }
  260. }
  261. break;
  262. case UA_ATTRIBUTEID_ACCESSLEVEL:
  263. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  264. v->hasValue = UA_TRUE;
  265. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->accessLevel,
  266. &UA_TYPES[UA_TYPES_BYTE]);
  267. break;
  268. case UA_ATTRIBUTEID_USERACCESSLEVEL:
  269. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  270. v->hasValue = UA_TRUE;
  271. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->userAccessLevel,
  272. &UA_TYPES[UA_TYPES_BYTE]);
  273. break;
  274. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  275. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  276. v->hasValue = UA_TRUE;
  277. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->minimumSamplingInterval,
  278. &UA_TYPES[UA_TYPES_DOUBLE]);
  279. break;
  280. case UA_ATTRIBUTEID_HISTORIZING:
  281. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  282. v->hasValue = UA_TRUE;
  283. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->historizing,
  284. &UA_TYPES[UA_TYPES_BOOLEAN]);
  285. break;
  286. case UA_ATTRIBUTEID_EXECUTABLE:
  287. CHECK_NODECLASS(UA_NODECLASS_METHOD);
  288. v->hasValue = UA_TRUE;
  289. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->executable,
  290. &UA_TYPES[UA_TYPES_BOOLEAN]);
  291. break;
  292. case UA_ATTRIBUTEID_USEREXECUTABLE:
  293. CHECK_NODECLASS(UA_NODECLASS_METHOD);
  294. v->hasValue = UA_TRUE;
  295. retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->userExecutable,
  296. &UA_TYPES[UA_TYPES_BOOLEAN]);
  297. break;
  298. default:
  299. v->hasStatus = UA_TRUE;
  300. v->status = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  301. break;
  302. }
  303. UA_NodeStore_release(node);
  304. if(retval != UA_STATUSCODE_GOOD) {
  305. v->hasStatus = UA_TRUE;
  306. v->status = retval;
  307. }
  308. handleServerTimestamps(timestamps, v);
  309. }
  310. void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
  311. UA_ReadResponse *response) {
  312. if(request->nodesToReadSize <= 0) {
  313. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  314. return;
  315. }
  316. if(request->timestampsToReturn > 3){
  317. response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
  318. return;
  319. }
  320. size_t size = request->nodesToReadSize;
  321. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_DATAVALUE], size);
  322. if(!response->results) {
  323. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  324. return;
  325. }
  326. response->resultsSize = size;
  327. if(request->maxAge < 0) {
  328. response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID;
  329. return;
  330. }
  331. /* ### Begin External Namespaces */
  332. UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
  333. UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
  334. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
  335. for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
  336. size_t indexSize = 0;
  337. for(size_t i = 0;i < size;i++) {
  338. if(request->nodesToRead[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
  339. continue;
  340. isExternal[i] = UA_TRUE;
  341. indices[indexSize] = i;
  342. indexSize++;
  343. }
  344. if(indexSize == 0)
  345. continue;
  346. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  347. ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
  348. indices, indexSize, response->results, UA_FALSE, response->diagnosticInfos);
  349. }
  350. /* ### End External Namespaces */
  351. for(size_t i = 0;i < size;i++) {
  352. if(!isExternal[i])
  353. readValue(server, request->timestampsToReturn, &request->nodesToRead[i], &response->results[i]);
  354. }
  355. #ifdef EXTENSION_STATELESS
  356. if(session==&anonymousSession){
  357. /* expiry header */
  358. UA_ExtensionObject additionalHeader;
  359. UA_ExtensionObject_init(&additionalHeader);
  360. additionalHeader.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
  361. UA_Variant variant;
  362. UA_Variant_init(&variant);
  363. variant.type = &UA_TYPES[UA_TYPES_DATETIME];
  364. variant.arrayLength = request->nodesToReadSize;
  365. UA_DateTime* expireArray = UA_NULL;
  366. expireArray = UA_Array_new(&UA_TYPES[UA_TYPES_DATETIME], request->nodesToReadSize);
  367. variant.data = expireArray;
  368. UA_ByteString str;
  369. UA_ByteString_init(&str);
  370. /*expires in 20 seconds*/
  371. for(UA_Int32 i = 0;i < response->resultsSize;i++) {
  372. expireArray[i] = UA_DateTime_now() + 20 * 100 * 1000 * 1000;
  373. }
  374. size_t offset = 0;
  375. str.data = UA_malloc(UA_Variant_calcSizeBinary(&variant));
  376. str.length = UA_Variant_calcSizeBinary(&variant);
  377. UA_Variant_encodeBinary(&variant, &str, &offset);
  378. additionalHeader.body = str;
  379. response->responseHeader.additionalHeader = additionalHeader;
  380. }
  381. #endif
  382. }
  383. static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
  384. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  385. /* is there a value at all */
  386. if(!wvalue->value.hasValue)
  387. return UA_STATUSCODE_BADTYPEMISMATCH;
  388. // we might repeat writing, e.g. when the node got replaced mid-work
  389. UA_Boolean done = UA_FALSE;
  390. while(!done) {
  391. const UA_Node *node = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
  392. if(!node)
  393. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  394. switch(wvalue->attributeId) {
  395. case UA_ATTRIBUTEID_NODEID:
  396. case UA_ATTRIBUTEID_NODECLASS:
  397. case UA_ATTRIBUTEID_BROWSENAME:
  398. case UA_ATTRIBUTEID_DISPLAYNAME:
  399. case UA_ATTRIBUTEID_DESCRIPTION:
  400. case UA_ATTRIBUTEID_WRITEMASK:
  401. case UA_ATTRIBUTEID_USERWRITEMASK:
  402. case UA_ATTRIBUTEID_ISABSTRACT:
  403. case UA_ATTRIBUTEID_SYMMETRIC:
  404. case UA_ATTRIBUTEID_INVERSENAME:
  405. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  406. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  407. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  408. break;
  409. case UA_ATTRIBUTEID_VALUE: {
  410. if(node->nodeClass != UA_NODECLASS_VARIABLE &&
  411. node->nodeClass != UA_NODECLASS_VARIABLETYPE) {
  412. retval = UA_STATUSCODE_BADTYPEMISMATCH;
  413. break;
  414. }
  415. /* parse the range */
  416. UA_Boolean hasRange = UA_FALSE;
  417. UA_NumericRange range;
  418. if(wvalue->indexRange.length > 0) {
  419. retval = parse_numericrange(wvalue->indexRange, &range);
  420. if(retval != UA_STATUSCODE_GOOD)
  421. break;
  422. hasRange = UA_TRUE;
  423. }
  424. /* the relevant members are similar for variables and variabletypes */
  425. const UA_VariableNode *vn = (const UA_VariableNode*)node;
  426. if(vn->valueSource == UA_VALUESOURCE_DATASOURCE) {
  427. if(!vn->value.dataSource.write) {
  428. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  429. break;
  430. }
  431. // todo: writing ranges
  432. retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value);
  433. done = UA_TRUE;
  434. break;
  435. }
  436. const UA_Variant *oldV = &vn->value.variant;
  437. /* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */
  438. if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) {
  439. if(oldV->type->namespaceZero && wvalue->value.value.type->namespaceZero &&
  440. oldV->type->typeIndex == wvalue->value.value.type->typeIndex)
  441. /* An enum was sent as an int32, or an opaque type as a bytestring. This is
  442. detected with the typeIndex indicated the "true" datatype. */
  443. wvalue->value.value.type = oldV->type;
  444. else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] &&
  445. (!oldV->data || vn->value.variant.arrayLength > -1) /* isArray */ &&
  446. wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
  447. wvalue->value.value.data && wvalue->value.value.arrayLength == -1 /* isScalar */) {
  448. /* a string is written to a byte array */
  449. UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data;
  450. wvalue->value.value.arrayLength = str->length;
  451. wvalue->value.value.data = str->data;
  452. wvalue->value.value.type = &UA_TYPES[UA_TYPES_BYTE];
  453. UA_free(str);
  454. } else {
  455. retval = UA_STATUSCODE_BADTYPEMISMATCH;
  456. break;
  457. }
  458. }
  459. /* copy the node */
  460. UA_VariableNode *newVn = (node->nodeClass == UA_NODECLASS_VARIABLE) ?
  461. UA_VariableNode_new() : (UA_VariableNode*)UA_VariableTypeNode_new();
  462. if(!newVn) {
  463. retval = UA_STATUSCODE_BADOUTOFMEMORY;
  464. break;
  465. }
  466. retval = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_copy(vn, newVn) :
  467. UA_VariableTypeNode_copy((const UA_VariableTypeNode*)vn, (UA_VariableTypeNode*)newVn);
  468. if(retval != UA_STATUSCODE_GOOD)
  469. goto clean_up;
  470. /* insert the new value*/
  471. if(hasRange)
  472. retval = UA_Variant_setRange(&newVn->value.variant, wvalue->value.value.data, range);
  473. else {
  474. UA_Variant_deleteMembers(&newVn->value.variant);
  475. retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant);
  476. }
  477. if(retval != UA_STATUSCODE_GOOD ||
  478. UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn,
  479. UA_NULL) != UA_STATUSCODE_GOOD)
  480. goto clean_up;
  481. if(hasRange)
  482. UA_free(range.dimensions);
  483. done = UA_TRUE;
  484. break;
  485. clean_up:
  486. if(node->nodeClass == UA_NODECLASS_VARIABLE)
  487. UA_VariableNode_delete(newVn);
  488. else
  489. UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn);
  490. if(hasRange)
  491. UA_free(range.dimensions);
  492. }
  493. break;
  494. case UA_ATTRIBUTEID_DATATYPE:
  495. case UA_ATTRIBUTEID_VALUERANK:
  496. case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
  497. case UA_ATTRIBUTEID_ACCESSLEVEL:
  498. case UA_ATTRIBUTEID_USERACCESSLEVEL:
  499. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  500. case UA_ATTRIBUTEID_HISTORIZING:
  501. case UA_ATTRIBUTEID_EXECUTABLE:
  502. case UA_ATTRIBUTEID_USEREXECUTABLE:
  503. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  504. break;
  505. default:
  506. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  507. break;
  508. }
  509. UA_NodeStore_release(node);
  510. if(retval != UA_STATUSCODE_GOOD)
  511. break;
  512. }
  513. return retval;
  514. }
  515. void Service_Write(UA_Server *server, UA_Session *session,
  516. const UA_WriteRequest *request, UA_WriteResponse *response) {
  517. UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
  518. if(request->nodesToWriteSize <= 0){
  519. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  520. return;
  521. }
  522. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_STATUSCODE], request->nodesToWriteSize);
  523. if(!response->results) {
  524. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  525. return;
  526. }
  527. /* ### Begin External Namespaces */
  528. UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToWriteSize);
  529. UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
  530. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToWriteSize);
  531. for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
  532. UA_UInt32 indexSize = 0;
  533. for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
  534. if(request->nodesToWrite[i].nodeId.namespaceIndex !=
  535. server->externalNamespaces[j].index)
  536. continue;
  537. isExternal[i] = UA_TRUE;
  538. indices[indexSize] = i;
  539. indexSize++;
  540. }
  541. if(indexSize == 0)
  542. continue;
  543. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  544. ens->writeNodes(ens->ensHandle, &request->requestHeader, request->nodesToWrite,
  545. indices, indexSize, response->results, response->diagnosticInfos);
  546. }
  547. /* ### End External Namespaces */
  548. response->resultsSize = request->nodesToWriteSize;
  549. for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
  550. if(!isExternal[i])
  551. response->results[i] = writeValue(server, &request->nodesToWrite[i]);
  552. }
  553. }