ua_services_attribute.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  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. /******************/
  8. /* Read Attribute */
  9. /******************/
  10. static size_t readNumber(UA_Byte *buf, UA_Int32 buflen, UA_UInt32 *number) {
  11. UA_UInt32 n = 0;
  12. size_t progress = 0;
  13. /* read numbers until the end or a non-number character appears */
  14. UA_Byte c;
  15. while((UA_Int32)progress < buflen) {
  16. c = buf[progress];
  17. if('0' > c || '9' < c)
  18. break;
  19. n = (n*10) + (c-'0');
  20. progress++;
  21. }
  22. *number = n;
  23. return progress;
  24. }
  25. static size_t readDimension(UA_Byte *buf, UA_Int32 buflen, struct UA_NumericRangeDimension *dim) {
  26. size_t progress = readNumber(buf, buflen, &dim->min);
  27. if(progress == 0)
  28. return 0;
  29. if(buflen <= (UA_Int32)progress || buf[progress] != ':') {
  30. dim->max = dim->min;
  31. return progress;
  32. }
  33. size_t progress2 = readNumber(&buf[progress+1], buflen - (progress + 1), &dim->max);
  34. if(progress2 == 0)
  35. return 0;
  36. return progress + progress2 + 1;
  37. }
  38. #ifndef BUILD_UNIT_TESTS
  39. static
  40. #endif
  41. UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range) {
  42. UA_Int32 index = 0;
  43. size_t dimensionsMax = 0;
  44. struct UA_NumericRangeDimension *dimensions = NULL;
  45. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  46. size_t pos = 0;
  47. do {
  48. /* alloc dimensions */
  49. if(index >= (UA_Int32)dimensionsMax) {
  50. struct UA_NumericRangeDimension *newds;
  51. newds = UA_realloc(dimensions, sizeof(struct UA_NumericRangeDimension) * (dimensionsMax + 2));
  52. if(!newds) {
  53. retval = UA_STATUSCODE_BADOUTOFMEMORY;
  54. break;
  55. }
  56. dimensions = newds;
  57. dimensionsMax = dimensionsMax + 2;
  58. }
  59. /* read the dimension */
  60. size_t progress = readDimension(&str->data[pos], str->length - pos, &dimensions[index]);
  61. if(progress == 0) {
  62. retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
  63. break;
  64. }
  65. pos += progress;
  66. index++;
  67. /* loop into the next dimension */
  68. if(pos >= str->length)
  69. break;
  70. } while(str->data[pos] == ',' && pos++);
  71. if(retval == UA_STATUSCODE_GOOD && index > 0) {
  72. range->dimensions = dimensions;
  73. range->dimensionsSize = index;
  74. } else
  75. UA_free(dimensions);
  76. return retval;
  77. }
  78. #define CHECK_NODECLASS(CLASS) \
  79. if(!(node->nodeClass & (CLASS))) { \
  80. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; \
  81. break; \
  82. }
  83. static void handleServerTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
  84. if(v && (timestamps == UA_TIMESTAMPSTORETURN_SERVER || timestamps == UA_TIMESTAMPSTORETURN_BOTH)) {
  85. v->hasServerTimestamp = UA_TRUE;
  86. v->serverTimestamp = UA_DateTime_now();
  87. }
  88. }
  89. static void handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
  90. if(timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
  91. v->hasSourceTimestamp = UA_TRUE;
  92. v->sourceTimestamp = UA_DateTime_now();
  93. }
  94. }
  95. /* force cast for zero-copy reading. ensure that the variant is never written into. */
  96. static void forceVariantSetScalar(UA_Variant *v, const void *p, const UA_DataType *type) {
  97. UA_Variant_init(v);
  98. v->type = type;
  99. v->data = (void*)(uintptr_t)p;
  100. v->storageType = UA_VARIANT_DATA_NODELETE;
  101. }
  102. static UA_StatusCode getVariableNodeValue(const UA_VariableNode *vn, const UA_TimestampsToReturn timestamps,
  103. const UA_ReadValueId *id, UA_DataValue *v) {
  104. UA_NumericRange range;
  105. UA_NumericRange *rangeptr = NULL;
  106. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  107. if(id->indexRange.length > 0) {
  108. retval = parse_numericrange(&id->indexRange, &range);
  109. if(retval != UA_STATUSCODE_GOOD)
  110. return retval;
  111. rangeptr = &range;
  112. }
  113. if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
  114. if(vn->value.variant.callback.onRead)
  115. vn->value.variant.callback.onRead(vn->value.variant.callback.handle, vn->nodeId,
  116. &v->value, rangeptr);
  117. if(!rangeptr) {
  118. v->value = vn->value.variant.value;
  119. v->value.storageType = UA_VARIANT_DATA_NODELETE;
  120. } else
  121. retval = UA_Variant_copyRange(&vn->value.variant.value, &v->value, range);
  122. if(retval == UA_STATUSCODE_GOOD)
  123. handleSourceTimestamps(timestamps, v);
  124. } else {
  125. UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
  126. timestamps == UA_TIMESTAMPSTORETURN_BOTH);
  127. retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId,
  128. sourceTimeStamp, rangeptr, v);
  129. }
  130. if(rangeptr)
  131. UA_free(range.dimensions);
  132. return retval;
  133. }
  134. static UA_StatusCode getVariableNodeDataType(const UA_VariableNode *vn, UA_DataValue *v) {
  135. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  136. if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
  137. forceVariantSetScalar(&v->value, &vn->value.variant.value.type->typeId,
  138. &UA_TYPES[UA_TYPES_NODEID]);
  139. } else {
  140. /* Read from the datasource to see the data type */
  141. UA_DataValue val;
  142. UA_DataValue_init(&val);
  143. retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, UA_FALSE, NULL, &val);
  144. if(retval != UA_STATUSCODE_GOOD)
  145. return retval;
  146. retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
  147. UA_DataValue_deleteMembers(&val);
  148. }
  149. return retval;
  150. }
  151. static UA_StatusCode getVariableNodeArrayDimensions(const UA_VariableNode *vn, UA_DataValue *v) {
  152. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  153. if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
  154. UA_Variant_setArray(&v->value, vn->value.variant.value.arrayDimensions,
  155. vn->value.variant.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
  156. v->value.storageType = UA_VARIANT_DATA_NODELETE;
  157. } else {
  158. /* Read the datasource to see the array dimensions */
  159. UA_DataValue val;
  160. UA_DataValue_init(&val);
  161. retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, UA_FALSE, NULL, &val);
  162. if(retval != UA_STATUSCODE_GOOD)
  163. return retval;
  164. retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions,
  165. val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
  166. UA_DataValue_deleteMembers(&val);
  167. }
  168. return retval;
  169. }
  170. static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"DefaultBinary"};
  171. /* clang complains about unused variables */
  172. // static const UA_String xmlEncoding = {sizeof("DefaultXml")-1, (UA_Byte*)"DefaultXml"};
  173. /** Reads a single attribute from a node in the nodestore. */
  174. void Service_Read_single(UA_Server *server, UA_Session *session, const UA_TimestampsToReturn timestamps,
  175. const UA_ReadValueId *id, UA_DataValue *v) {
  176. if(id->dataEncoding.name.length > 0 && !UA_String_equal(&binEncoding, &id->dataEncoding.name)) {
  177. v->hasStatus = UA_TRUE;
  178. v->status = UA_STATUSCODE_BADDATAENCODINGINVALID;
  179. return;
  180. }
  181. //index range for a non-value
  182. if(id->indexRange.length > 0 && id->attributeId != UA_ATTRIBUTEID_VALUE){
  183. v->hasStatus = UA_TRUE;
  184. v->status = UA_STATUSCODE_BADINDEXRANGENODATA;
  185. return;
  186. }
  187. UA_Node const *node = UA_NodeStore_get(server->nodestore, &id->nodeId);
  188. if(!node) {
  189. v->hasStatus = UA_TRUE;
  190. v->status = UA_STATUSCODE_BADNODEIDUNKNOWN;
  191. return;
  192. }
  193. /* When setting the value fails in the switch, we get an error code and set hasValue to false */
  194. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  195. v->hasValue = UA_TRUE;
  196. switch(id->attributeId) {
  197. case UA_ATTRIBUTEID_NODEID:
  198. forceVariantSetScalar(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
  199. break;
  200. case UA_ATTRIBUTEID_NODECLASS:
  201. forceVariantSetScalar(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
  202. break;
  203. case UA_ATTRIBUTEID_BROWSENAME:
  204. forceVariantSetScalar(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
  205. break;
  206. case UA_ATTRIBUTEID_DISPLAYNAME:
  207. forceVariantSetScalar(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  208. break;
  209. case UA_ATTRIBUTEID_DESCRIPTION:
  210. forceVariantSetScalar(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  211. break;
  212. case UA_ATTRIBUTEID_WRITEMASK:
  213. forceVariantSetScalar(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
  214. break;
  215. case UA_ATTRIBUTEID_USERWRITEMASK:
  216. forceVariantSetScalar(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
  217. break;
  218. case UA_ATTRIBUTEID_ISABSTRACT:
  219. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE |
  220. UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
  221. forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->isAbstract,
  222. &UA_TYPES[UA_TYPES_BOOLEAN]);
  223. break;
  224. case UA_ATTRIBUTEID_SYMMETRIC:
  225. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
  226. forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric,
  227. &UA_TYPES[UA_TYPES_BOOLEAN]);
  228. break;
  229. case UA_ATTRIBUTEID_INVERSENAME:
  230. CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
  231. forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName,
  232. &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
  233. break;
  234. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  235. CHECK_NODECLASS(UA_NODECLASS_VIEW);
  236. forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->containsNoLoops,
  237. &UA_TYPES[UA_TYPES_BOOLEAN]);
  238. break;
  239. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  240. CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
  241. forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->eventNotifier,
  242. &UA_TYPES[UA_TYPES_BYTE]);
  243. break;
  244. case UA_ATTRIBUTEID_VALUE:
  245. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  246. retval = getVariableNodeValue((const UA_VariableNode*)node, timestamps, id, v);
  247. break;
  248. case UA_ATTRIBUTEID_DATATYPE:
  249. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  250. retval = getVariableNodeDataType((const UA_VariableNode*)node, v);
  251. break;
  252. case UA_ATTRIBUTEID_VALUERANK:
  253. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  254. forceVariantSetScalar(&v->value, &((const UA_VariableTypeNode*)node)->valueRank,
  255. &UA_TYPES[UA_TYPES_INT32]);
  256. break;
  257. case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
  258. CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  259. retval = getVariableNodeArrayDimensions((const UA_VariableNode*)node, v);
  260. break;
  261. case UA_ATTRIBUTEID_ACCESSLEVEL:
  262. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  263. forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->accessLevel,
  264. &UA_TYPES[UA_TYPES_BYTE]);
  265. break;
  266. case UA_ATTRIBUTEID_USERACCESSLEVEL:
  267. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  268. forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->userAccessLevel,
  269. &UA_TYPES[UA_TYPES_BYTE]);
  270. break;
  271. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  272. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  273. forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval,
  274. &UA_TYPES[UA_TYPES_DOUBLE]);
  275. break;
  276. case UA_ATTRIBUTEID_HISTORIZING:
  277. CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
  278. forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->historizing,
  279. &UA_TYPES[UA_TYPES_BOOLEAN]);
  280. break;
  281. case UA_ATTRIBUTEID_EXECUTABLE:
  282. CHECK_NODECLASS(UA_NODECLASS_METHOD);
  283. forceVariantSetScalar(&v->value, &((const UA_MethodNode*)node)->executable,
  284. &UA_TYPES[UA_TYPES_BOOLEAN]);
  285. break;
  286. case UA_ATTRIBUTEID_USEREXECUTABLE:
  287. CHECK_NODECLASS(UA_NODECLASS_METHOD);
  288. forceVariantSetScalar(&v->value, &((const UA_MethodNode*)node)->userExecutable,
  289. &UA_TYPES[UA_TYPES_BOOLEAN]);
  290. break;
  291. default:
  292. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  293. break;
  294. }
  295. if(retval != UA_STATUSCODE_GOOD) {
  296. v->hasValue = UA_FALSE;
  297. v->hasStatus = UA_TRUE;
  298. v->status = retval;
  299. }
  300. // Todo: what if the timestamp from the datasource are already present?
  301. handleServerTimestamps(timestamps, v);
  302. }
  303. void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
  304. UA_ReadResponse *response) {
  305. UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
  306. "Processing ReadRequest for Session (ns=%i,i=%i)",
  307. session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
  308. if(request->nodesToReadSize <= 0) {
  309. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  310. return;
  311. }
  312. if(request->timestampsToReturn > 3){
  313. response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
  314. return;
  315. }
  316. size_t size = request->nodesToReadSize;
  317. response->results = UA_Array_new(size, &UA_TYPES[UA_TYPES_DATAVALUE]);
  318. if(!response->results) {
  319. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  320. return;
  321. }
  322. response->resultsSize = size;
  323. if(request->maxAge < 0) {
  324. response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID;
  325. return;
  326. }
  327. #ifdef UA_EXTERNAL_NAMESPACES
  328. UA_Boolean isExternal[size];
  329. UA_UInt32 indices[size];
  330. memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
  331. for(size_t j = 0;j<server->externalNamespacesSize;j++) {
  332. size_t indexSize = 0;
  333. for(size_t i = 0;i < size;i++) {
  334. if(request->nodesToRead[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
  335. continue;
  336. isExternal[i] = UA_TRUE;
  337. indices[indexSize] = i;
  338. indexSize++;
  339. }
  340. if(indexSize == 0)
  341. continue;
  342. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  343. ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
  344. indices, indexSize, response->results, UA_FALSE, response->diagnosticInfos);
  345. }
  346. #endif
  347. for(size_t i = 0;i < size;i++) {
  348. #ifdef UA_EXTERNAL_NAMESPACES
  349. if(!isExternal[i])
  350. #endif
  351. Service_Read_single(server, session, request->timestampsToReturn,
  352. &request->nodesToRead[i], &response->results[i]);
  353. }
  354. #ifdef EXTENSION_STATELESS
  355. /* Add an expiry header for caching */
  356. if(session==&anonymousSession){
  357. UA_ExtensionObject additionalHeader;
  358. UA_ExtensionObject_init(&additionalHeader);
  359. additionalHeader.typeId = UA_TYPES[UA_TYPES_VARIANT].typeId;
  360. additionalHeader.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
  361. UA_Variant variant;
  362. UA_Variant_init(&variant);
  363. UA_DateTime* expireArray = NULL;
  364. expireArray = UA_Array_new(&UA_TYPES[UA_TYPES_DATETIME], request->nodesToReadSize);
  365. variant.data = expireArray;
  366. /*expires in 20 seconds*/
  367. for(UA_Int32 i = 0;i < response->resultsSize;i++) {
  368. expireArray[i] = UA_DateTime_now() + 20 * 100 * 1000 * 1000;
  369. }
  370. UA_Variant_setArray(&variant, expireArray, request->nodesToReadSize, &UA_TYPES[UA_TYPES_DATETIME]);
  371. size_t offset = 0;
  372. UA_ByteString str;
  373. UA_ByteString_newMembers(&str, 65536);
  374. UA_Variant_encodeBinary(&variant, &str, &offset);
  375. UA_Array_delete(expireArray, &UA_TYPES[UA_TYPES_DATETIME], request->nodesToReadSize);
  376. additionalHeader.body = str;
  377. additionalHeader.body.length = offset;
  378. response->responseHeader.additionalHeader = additionalHeader;
  379. }
  380. #endif
  381. }
  382. /*******************/
  383. /* Write Attribute */
  384. /*******************/
  385. UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
  386. UA_EditNodeCallback callback, const void *data) {
  387. UA_StatusCode retval;
  388. do {
  389. UA_MT_CONST UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
  390. if(!node)
  391. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  392. #ifndef UA_MULTITHREADING
  393. retval = callback(server, session, node, data);
  394. return retval;
  395. #else
  396. UA_Node *copy = UA_Node_copyAnyNodeClass(node);
  397. if(!copy)
  398. return UA_STATUSCODE_BADOUTOFMEMORY;
  399. retval = callback(server, session, copy, data);
  400. if(retval != UA_STATUSCODE_GOOD) {
  401. UA_Node_deleteAnyNodeClass(copy);
  402. return retval;
  403. }
  404. retval = UA_NodeStore_replace(server->nodestore, node, copy, NULL);
  405. if(retval != UA_STATUSCODE_GOOD)
  406. UA_Node_deleteAnyNodeClass(copy);
  407. #endif
  408. } while(retval != UA_STATUSCODE_GOOD);
  409. return UA_STATUSCODE_GOOD;
  410. }
  411. #define CHECK_DATATYPE(EXP_DT) \
  412. if(!wvalue->value.hasValue || \
  413. &UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \
  414. !UA_Variant_isScalar(&wvalue->value.value)) { \
  415. retval = UA_STATUSCODE_BADTYPEMISMATCH; \
  416. break; \
  417. }
  418. #define CHECK_NODECLASS_WRITE(CLASS) \
  419. if((node->nodeClass & (CLASS)) == 0) { \
  420. retval = UA_STATUSCODE_BADNODECLASSINVALID; \
  421. break; \
  422. }
  423. static UA_StatusCode
  424. Service_Write_single_ValueDataSource(UA_Server *server, UA_Session *session, const UA_VariableNode *node,
  425. const UA_WriteValue *wvalue) {
  426. UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
  427. UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
  428. UA_assert(node->valueSource == UA_VALUESOURCE_DATASOURCE);
  429. UA_StatusCode retval;
  430. if(wvalue->indexRange.length <= 0) {
  431. retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
  432. &wvalue->value.value, NULL);
  433. } else {
  434. UA_NumericRange range;
  435. retval = parse_numericrange(&wvalue->indexRange, &range);
  436. if(retval != UA_STATUSCODE_GOOD)
  437. return retval;
  438. retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
  439. &wvalue->value.value, &range);
  440. UA_free(range.dimensions);
  441. }
  442. return retval;
  443. }
  444. enum type_equivalence {
  445. TYPE_EQUIVALENCE_NONE,
  446. TYPE_EQUIVALENCE_ENUM,
  447. TYPE_EQUIVALENCE_OPAQUE
  448. };
  449. static enum type_equivalence typeEquivalence(const UA_DataType *type) {
  450. if(type->membersSize != 1 || !type->members[0].namespaceZero)
  451. return TYPE_EQUIVALENCE_NONE;
  452. if(type->members[0].memberTypeIndex == UA_TYPES_INT32)
  453. return TYPE_EQUIVALENCE_ENUM;
  454. if(type->members[0].memberTypeIndex == UA_TYPES_BYTE && type->members[0].isArray)
  455. return TYPE_EQUIVALENCE_OPAQUE;
  456. return TYPE_EQUIVALENCE_NONE;
  457. }
  458. /* In the multithreaded case, node is a copy */
  459. static UA_StatusCode
  460. CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) {
  461. UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
  462. UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
  463. UA_assert(node->valueSource == UA_VALUESOURCE_VARIANT);
  464. /* Parse the range */
  465. UA_NumericRange range;
  466. UA_NumericRange *rangeptr = NULL;
  467. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  468. if(wvalue->indexRange.length > 0) {
  469. retval = parse_numericrange(&wvalue->indexRange, &range);
  470. if(retval != UA_STATUSCODE_GOOD)
  471. return retval;
  472. rangeptr = &range;
  473. }
  474. /* The nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings.
  475. nodeV contains the correct type definition. */
  476. const UA_Variant *newV = &wvalue->value.value;
  477. UA_Variant *oldV = &node->value.variant.value;
  478. UA_Variant cast_v;
  479. if(!UA_NodeId_equal(&oldV->type->typeId, &newV->type->typeId)) {
  480. cast_v = wvalue->value.value;
  481. newV = &cast_v;
  482. enum type_equivalence te1 = typeEquivalence(oldV->type);
  483. enum type_equivalence te2 = typeEquivalence(newV->type);
  484. if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
  485. /* An enum was sent as an int32, or an opaque type as a bytestring. This is
  486. detected with the typeIndex indicated the "true" datatype. */
  487. cast_v.type = oldV->type;
  488. } else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
  489. newV->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(newV)) {
  490. /* a string is written to a byte array */
  491. UA_ByteString *str = (UA_ByteString*) newV->data;
  492. cast_v.arrayLength = str->length;
  493. cast_v.data = str->data;
  494. cast_v.type = &UA_TYPES[UA_TYPES_BYTE];
  495. } else {
  496. if(rangeptr)
  497. UA_free(range.dimensions);
  498. return UA_STATUSCODE_BADTYPEMISMATCH;
  499. }
  500. }
  501. if(!rangeptr) {
  502. UA_Variant_deleteMembers(&node->value.variant.value);
  503. UA_Variant_copy(newV, &node->value.variant.value);
  504. } else
  505. retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data, newV->arrayLength, range);
  506. if(node->value.variant.callback.onWrite)
  507. node->value.variant.callback.onWrite(node->value.variant.callback.handle, node->nodeId,
  508. &node->value.variant.value, rangeptr);
  509. if(rangeptr)
  510. UA_free(range.dimensions);
  511. return retval;
  512. }
  513. static UA_StatusCode
  514. CopyAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, const UA_WriteValue *wvalue) {
  515. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  516. void *value = wvalue->value.value.data;
  517. void *target;
  518. const UA_DataType *type = NULL;
  519. switch(wvalue->attributeId) {
  520. case UA_ATTRIBUTEID_NODEID:
  521. case UA_ATTRIBUTEID_NODECLASS:
  522. case UA_ATTRIBUTEID_DATATYPE:
  523. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  524. break;
  525. case UA_ATTRIBUTEID_BROWSENAME:
  526. CHECK_DATATYPE(QUALIFIEDNAME);
  527. target = &node->browseName;
  528. type = &UA_TYPES[UA_TYPES_QUALIFIEDNAME];
  529. break;
  530. case UA_ATTRIBUTEID_DISPLAYNAME:
  531. CHECK_DATATYPE(LOCALIZEDTEXT);
  532. target = &node->displayName;
  533. type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
  534. break;
  535. case UA_ATTRIBUTEID_DESCRIPTION:
  536. CHECK_DATATYPE(LOCALIZEDTEXT);
  537. target = &node->description;
  538. type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
  539. break;
  540. case UA_ATTRIBUTEID_WRITEMASK:
  541. CHECK_DATATYPE(UINT32);
  542. node->writeMask = *(UA_UInt32*)value;
  543. break;
  544. case UA_ATTRIBUTEID_USERWRITEMASK:
  545. CHECK_DATATYPE(UINT32);
  546. node->userWriteMask = *(UA_UInt32*)value;
  547. break;
  548. case UA_ATTRIBUTEID_ISABSTRACT:
  549. CHECK_NODECLASS_WRITE(UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_REFERENCETYPE |
  550. UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
  551. CHECK_DATATYPE(BOOLEAN);
  552. ((UA_ObjectTypeNode*)node)->isAbstract = *(UA_Boolean*)value;
  553. break;
  554. case UA_ATTRIBUTEID_SYMMETRIC:
  555. CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
  556. CHECK_DATATYPE(BOOLEAN);
  557. ((UA_ReferenceTypeNode*)node)->symmetric = *(UA_Boolean*)value;
  558. break;
  559. case UA_ATTRIBUTEID_INVERSENAME:
  560. CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
  561. CHECK_DATATYPE(LOCALIZEDTEXT);
  562. target = &((UA_ReferenceTypeNode*)node)->inverseName;
  563. type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
  564. break;
  565. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  566. CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
  567. CHECK_DATATYPE(BOOLEAN);
  568. ((UA_ViewNode*)node)->containsNoLoops = *(UA_Boolean*)value;
  569. break;
  570. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  571. CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
  572. CHECK_DATATYPE(BYTE);
  573. ((UA_ViewNode*)node)->eventNotifier = *(UA_Byte*)value;
  574. break;
  575. case UA_ATTRIBUTEID_VALUE:
  576. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  577. retval = CopyValueIntoNode((UA_VariableNode*)node, wvalue);
  578. break;
  579. case UA_ATTRIBUTEID_ACCESSLEVEL:
  580. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  581. CHECK_DATATYPE(BYTE);
  582. ((UA_VariableNode*)node)->accessLevel = *(UA_Byte*)value;
  583. break;
  584. case UA_ATTRIBUTEID_USERACCESSLEVEL:
  585. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  586. CHECK_DATATYPE(BYTE);
  587. ((UA_VariableNode*)node)->userAccessLevel = *(UA_Byte*)value;
  588. break;
  589. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  590. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  591. CHECK_DATATYPE(DOUBLE);
  592. ((UA_VariableNode*)node)->minimumSamplingInterval = *(UA_Double*)value;
  593. break;
  594. case UA_ATTRIBUTEID_HISTORIZING:
  595. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  596. CHECK_DATATYPE(BOOLEAN);
  597. ((UA_VariableNode*)node)->historizing = *(UA_Boolean*)value;
  598. break;
  599. case UA_ATTRIBUTEID_EXECUTABLE:
  600. CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
  601. CHECK_DATATYPE(BOOLEAN);
  602. ((UA_MethodNode*)node)->executable = *(UA_Boolean*)value;
  603. break;
  604. case UA_ATTRIBUTEID_USEREXECUTABLE:
  605. CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
  606. CHECK_DATATYPE(BOOLEAN);
  607. ((UA_MethodNode*)node)->userExecutable = *(UA_Boolean*)value;
  608. break;
  609. default:
  610. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  611. break;
  612. }
  613. if(type) {
  614. UA_deleteMembers(target, type);
  615. retval = UA_copy(value, target, type);
  616. }
  617. return retval;
  618. }
  619. UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, const UA_WriteValue *wvalue) {
  620. if(!wvalue->value.hasValue || !wvalue->value.value.data)
  621. return UA_STATUSCODE_BADNODATA; // TODO: is this the right return code?
  622. if(wvalue->attributeId == UA_ATTRIBUTEID_VALUE) {
  623. const UA_Node *orig = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
  624. if(!orig)
  625. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  626. if(orig->nodeClass & (UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLE) &&
  627. ((const UA_VariableNode*)orig)->valueSource == UA_VALUESOURCE_DATASOURCE) {
  628. UA_StatusCode retval =
  629. Service_Write_single_ValueDataSource(server, session, (const UA_VariableNode*)orig, wvalue);
  630. return retval;
  631. }
  632. }
  633. return UA_Server_editNode(server, session, &wvalue->nodeId,
  634. (UA_EditNodeCallback)CopyAttributeIntoNode, wvalue);
  635. }
  636. void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
  637. UA_WriteResponse *response) {
  638. UA_assert(server != NULL && session != NULL && request != NULL && response != NULL);
  639. UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
  640. "Processing WriteRequest for Session (ns=%i,i=%i)",
  641. session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
  642. if(request->nodesToWriteSize <= 0) {
  643. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  644. return;
  645. }
  646. response->results = UA_Array_new(request->nodesToWriteSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  647. if(!response->results) {
  648. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  649. return;
  650. }
  651. #ifdef UA_EXTERNAL_NAMESPACES
  652. UA_Boolean isExternal[request->nodesToWriteSize];
  653. UA_UInt32 indices[request->nodesToWriteSize];
  654. memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
  655. for(size_t j = 0; j < server->externalNamespacesSize; j++) {
  656. UA_UInt32 indexSize = 0;
  657. for(UA_Int32 i = 0; i < request->nodesToWriteSize; i++) {
  658. if(request->nodesToWrite[i].nodeId.namespaceIndex !=
  659. server->externalNamespaces[j].index)
  660. continue;
  661. isExternal[i] = UA_TRUE;
  662. indices[indexSize] = i;
  663. indexSize++;
  664. }
  665. if(indexSize == 0)
  666. continue;
  667. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  668. ens->writeNodes(ens->ensHandle, &request->requestHeader, request->nodesToWrite,
  669. indices, indexSize, response->results, response->diagnosticInfos);
  670. }
  671. #endif
  672. response->resultsSize = request->nodesToWriteSize;
  673. for(size_t i = 0;i < request->nodesToWriteSize;i++) {
  674. #ifdef UA_EXTERNAL_NAMESPACES
  675. if(!isExternal[i])
  676. #endif
  677. response->results[i] = Service_Write_single(server, session, &request->nodesToWrite[i]);
  678. }
  679. }