ua_services_attribute.c 29 KB

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