ua_services_attribute.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  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. UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
  291. "Processing ReadRequest for Session (ns=%i,i=%i)",
  292. session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
  293. if(request->nodesToReadSize <= 0) {
  294. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  295. return;
  296. }
  297. if(request->timestampsToReturn > 3){
  298. response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
  299. return;
  300. }
  301. size_t size = request->nodesToReadSize;
  302. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_DATAVALUE], size);
  303. if(!response->results) {
  304. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  305. return;
  306. }
  307. response->resultsSize = size;
  308. if(request->maxAge < 0) {
  309. response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID;
  310. return;
  311. }
  312. #ifdef UA_EXTERNAL_NAMESPACES
  313. UA_Boolean isExternal[size];
  314. UA_UInt32 indices[size];
  315. UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
  316. for(size_t j = 0;j<server->externalNamespacesSize;j++) {
  317. size_t indexSize = 0;
  318. for(size_t i = 0;i < size;i++) {
  319. if(request->nodesToRead[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
  320. continue;
  321. isExternal[i] = UA_TRUE;
  322. indices[indexSize] = i;
  323. indexSize++;
  324. }
  325. if(indexSize == 0)
  326. continue;
  327. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  328. ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
  329. indices, indexSize, response->results, UA_FALSE, response->diagnosticInfos);
  330. }
  331. #endif
  332. for(size_t i = 0;i < size;i++) {
  333. #ifdef UA_EXTERNAL_NAMESPACES
  334. if(!isExternal[i])
  335. #endif
  336. Service_Read_single(server, session, request->timestampsToReturn,
  337. &request->nodesToRead[i], &response->results[i]);
  338. }
  339. #ifdef EXTENSION_STATELESS
  340. if(session==&anonymousSession){
  341. /* expiry header */
  342. UA_ExtensionObject additionalHeader;
  343. UA_ExtensionObject_init(&additionalHeader);
  344. additionalHeader.typeId = UA_TYPES[UA_TYPES_VARIANT].typeId;
  345. additionalHeader.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
  346. UA_Variant variant;
  347. UA_Variant_init(&variant);
  348. UA_DateTime* expireArray = UA_NULL;
  349. expireArray = UA_Array_new(&UA_TYPES[UA_TYPES_DATETIME], request->nodesToReadSize);
  350. variant.data = expireArray;
  351. /*expires in 20 seconds*/
  352. for(UA_Int32 i = 0;i < response->resultsSize;i++) {
  353. expireArray[i] = UA_DateTime_now() + 20 * 100 * 1000 * 1000;
  354. }
  355. UA_Variant_setArray(&variant, expireArray, request->nodesToReadSize, &UA_TYPES[UA_TYPES_DATETIME]);
  356. size_t offset = 0;
  357. UA_ByteString str;
  358. UA_ByteString_newMembers(&str, 65536);
  359. UA_Variant_encodeBinary(&variant, &str, &offset);
  360. UA_Array_delete(expireArray, &UA_TYPES[UA_TYPES_DATETIME], request->nodesToReadSize);
  361. additionalHeader.body = str;
  362. additionalHeader.body.length = offset;
  363. response->responseHeader.additionalHeader = additionalHeader;
  364. }
  365. #endif
  366. }
  367. /*******************/
  368. /* Write Attribute */
  369. /*******************/
  370. UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
  371. UA_EditNodeCallback callback, const void *data) {
  372. UA_StatusCode retval;
  373. do {
  374. const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
  375. if(!node)
  376. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  377. #ifndef UA_MULTITHREADING
  378. retval = callback(server, session, (UA_Node*)(uintptr_t)node, data);
  379. UA_NodeStore_release(node);
  380. return retval;
  381. #else
  382. UA_Node *copy = UA_Node_copyAnyNodeClass(node);
  383. UA_NodeStore_release(node);
  384. if(!copy)
  385. return UA_STATUSCODE_BADOUTOFMEMORY;
  386. retval = callback(server, session, copy, data);
  387. if(retval != UA_STATUSCODE_GOOD) {
  388. UA_Node_deleteAnyNodeClass(copy);
  389. return retval;
  390. }
  391. retval = UA_NodeStore_replace(server->nodestore, node, copy, UA_NULL);
  392. if(retval != UA_STATUSCODE_GOOD)
  393. UA_Node_deleteAnyNodeClass(copy);
  394. #endif
  395. } while(retval != UA_STATUSCODE_GOOD);
  396. return UA_STATUSCODE_GOOD;
  397. }
  398. #define CHECK_DATATYPE(EXP_DT) \
  399. if(!wvalue->value.hasValue || \
  400. &UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \
  401. !UA_Variant_isScalar(&wvalue->value.value)) { \
  402. retval = UA_STATUSCODE_BADTYPEMISMATCH; \
  403. break; \
  404. }
  405. #define CHECK_NODECLASS_WRITE(CLASS) \
  406. if((node->nodeClass & (CLASS)) == 0) { \
  407. retval = UA_STATUSCODE_BADNODECLASSINVALID; \
  408. break; \
  409. }
  410. static UA_StatusCode
  411. Service_Write_single_ValueDataSource(UA_Server *server, UA_Session *session, const UA_VariableNode *node,
  412. UA_WriteValue *wvalue) {
  413. UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
  414. UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
  415. UA_assert(node->valueSource == UA_VALUESOURCE_DATASOURCE);
  416. UA_StatusCode retval;
  417. if(wvalue->indexRange.length <= 0) {
  418. retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
  419. &wvalue->value.value, UA_NULL);
  420. } else {
  421. UA_NumericRange range;
  422. retval = parse_numericrange(wvalue->indexRange, &range);
  423. if(retval != UA_STATUSCODE_GOOD)
  424. return retval;
  425. retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
  426. &wvalue->value.value, &range);
  427. UA_free(range.dimensions);
  428. }
  429. return retval;
  430. }
  431. /* In the multithreaded case, node is a copy */
  432. static UA_StatusCode
  433. MoveValueIntoNode(UA_Server *server, UA_Session *session, UA_VariableNode *node, UA_WriteValue *wvalue) {
  434. UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
  435. UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
  436. UA_assert(node->valueSource == UA_VALUESOURCE_VARIANT);
  437. /* Parse the range */
  438. UA_NumericRange range;
  439. UA_NumericRange *rangeptr = UA_NULL;
  440. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  441. if(wvalue->indexRange.length > 0) {
  442. retval = parse_numericrange(wvalue->indexRange, &range);
  443. if(retval != UA_STATUSCODE_GOOD)
  444. return retval;
  445. rangeptr = &range;
  446. }
  447. /* The nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings.
  448. nodeV contains the correct type definition. */
  449. UA_Variant *newV = &wvalue->value.value;
  450. UA_Variant *oldV = &node->value.variant.value;
  451. UA_Variant cast_v;
  452. if(!UA_NodeId_equal(&oldV->type->typeId, &newV->type->typeId)) {
  453. cast_v = wvalue->value.value;
  454. newV = &cast_v;
  455. if(oldV->type->namespaceZero && newV->type->namespaceZero &&
  456. oldV->type->typeIndex == newV->type->typeIndex) {
  457. /* An enum was sent as an int32, or an opaque type as a bytestring. This is
  458. detected with the typeIndex indicated the "true" datatype. */
  459. newV->type = oldV->type;
  460. } else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
  461. newV->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(newV)) {
  462. /* a string is written to a byte array */
  463. UA_ByteString *str = (UA_ByteString*) newV->data;
  464. newV->arrayLength = str->length;
  465. newV->data = str->data;
  466. newV->type = &UA_TYPES[UA_TYPES_BYTE];
  467. } else {
  468. if(rangeptr)
  469. UA_free(range.dimensions);
  470. return UA_STATUSCODE_BADTYPEMISMATCH;
  471. }
  472. }
  473. if(!rangeptr) {
  474. // TODO: Avoid copying the whole node and then delete the old value for multithreading
  475. UA_Variant_deleteMembers(&node->value.variant.value);
  476. node->value.variant.value = *newV;
  477. UA_Variant_init(&wvalue->value.value);
  478. } else {
  479. retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data, newV->arrayLength, range);
  480. }
  481. if(node->value.variant.callback.onWrite)
  482. node->value.variant.callback.onWrite(node->value.variant.callback.handle, node->nodeId,
  483. &node->value.variant.value, rangeptr);
  484. if(rangeptr)
  485. UA_free(range.dimensions);
  486. return retval;
  487. }
  488. static UA_StatusCode
  489. MoveAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, UA_WriteValue *wvalue) {
  490. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  491. void *value = wvalue->value.value.data;
  492. switch(wvalue->attributeId) {
  493. case UA_ATTRIBUTEID_NODEID:
  494. case UA_ATTRIBUTEID_NODECLASS:
  495. case UA_ATTRIBUTEID_DATATYPE:
  496. retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
  497. break;
  498. case UA_ATTRIBUTEID_BROWSENAME:
  499. CHECK_DATATYPE(QUALIFIEDNAME);
  500. UA_QualifiedName_deleteMembers(&node->browseName);
  501. node->browseName = *(UA_QualifiedName*)value;
  502. UA_QualifiedName_init((UA_QualifiedName*)value);
  503. break;
  504. case UA_ATTRIBUTEID_DISPLAYNAME:
  505. CHECK_DATATYPE(LOCALIZEDTEXT);
  506. UA_LocalizedText_deleteMembers(&node->displayName);
  507. node->displayName = *(UA_LocalizedText*)value;
  508. UA_LocalizedText_init((UA_LocalizedText*)value);
  509. break;
  510. case UA_ATTRIBUTEID_DESCRIPTION:
  511. CHECK_DATATYPE(LOCALIZEDTEXT);
  512. UA_LocalizedText_deleteMembers(&node->description);
  513. node->description = *(UA_LocalizedText*)value;
  514. UA_LocalizedText_init((UA_LocalizedText*)value);
  515. break;
  516. case UA_ATTRIBUTEID_WRITEMASK:
  517. CHECK_DATATYPE(UINT32);
  518. node->writeMask = *(UA_UInt32*)value;
  519. break;
  520. case UA_ATTRIBUTEID_USERWRITEMASK:
  521. CHECK_DATATYPE(UINT32);
  522. node->userWriteMask = *(UA_UInt32*)value;
  523. break;
  524. case UA_ATTRIBUTEID_ISABSTRACT:
  525. CHECK_NODECLASS_WRITE(UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_REFERENCETYPE |
  526. UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
  527. CHECK_DATATYPE(BOOLEAN);
  528. ((UA_ObjectTypeNode*)node)->isAbstract = *(UA_Boolean*)value;
  529. break;
  530. case UA_ATTRIBUTEID_SYMMETRIC:
  531. CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
  532. CHECK_DATATYPE(BOOLEAN);
  533. ((UA_ReferenceTypeNode*)node)->symmetric = *(UA_Boolean*)value;
  534. break;
  535. case UA_ATTRIBUTEID_INVERSENAME:
  536. CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
  537. CHECK_DATATYPE(LOCALIZEDTEXT);
  538. UA_ReferenceTypeNode *n = (UA_ReferenceTypeNode*)node;
  539. UA_LocalizedText_deleteMembers(&n->inverseName);
  540. n->inverseName = *(UA_LocalizedText*)value;
  541. UA_LocalizedText_init((UA_LocalizedText*)value);
  542. break;
  543. case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
  544. CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
  545. CHECK_DATATYPE(BOOLEAN);
  546. ((UA_ViewNode*)node)->containsNoLoops = *(UA_Boolean*)value;
  547. break;
  548. case UA_ATTRIBUTEID_EVENTNOTIFIER:
  549. CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
  550. CHECK_DATATYPE(BYTE);
  551. ((UA_ViewNode*)node)->eventNotifier = *(UA_Byte*)value;
  552. break;
  553. case UA_ATTRIBUTEID_VALUE:
  554. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
  555. retval = MoveValueIntoNode(server, session, (UA_VariableNode*)node, wvalue);
  556. break;
  557. case UA_ATTRIBUTEID_ACCESSLEVEL:
  558. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  559. CHECK_DATATYPE(BYTE);
  560. ((UA_VariableNode*)node)->accessLevel = *(UA_Byte*)value;
  561. break;
  562. case UA_ATTRIBUTEID_USERACCESSLEVEL:
  563. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  564. CHECK_DATATYPE(BYTE);
  565. ((UA_VariableNode*)node)->userAccessLevel = *(UA_Byte*)value;
  566. break;
  567. case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
  568. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  569. CHECK_DATATYPE(DOUBLE);
  570. ((UA_VariableNode*)node)->minimumSamplingInterval = *(UA_Double*)value;
  571. break;
  572. case UA_ATTRIBUTEID_HISTORIZING:
  573. CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
  574. CHECK_DATATYPE(BOOLEAN);
  575. ((UA_VariableNode*)node)->historizing = *(UA_Boolean*)value;
  576. break;
  577. case UA_ATTRIBUTEID_EXECUTABLE:
  578. CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
  579. CHECK_DATATYPE(BOOLEAN);
  580. ((UA_MethodNode*)node)->executable = *(UA_Boolean*)value;
  581. break;
  582. case UA_ATTRIBUTEID_USEREXECUTABLE:
  583. CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
  584. CHECK_DATATYPE(BOOLEAN);
  585. ((UA_MethodNode*)node)->userExecutable = *(UA_Boolean*)value;
  586. break;
  587. default:
  588. retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
  589. break;
  590. }
  591. return retval;
  592. }
  593. UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, UA_WriteValue *wvalue) {
  594. if(!wvalue->value.hasValue || !wvalue->value.value.data)
  595. return UA_STATUSCODE_BADNODATA; // TODO: is this the right return code?
  596. if(wvalue->attributeId == UA_ATTRIBUTEID_VALUE) {
  597. const UA_Node *orig = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
  598. if(!orig)
  599. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  600. if(orig->nodeClass & (UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLE) &&
  601. ((const UA_VariableNode*)orig)->valueSource == UA_VALUESOURCE_DATASOURCE) {
  602. UA_StatusCode retval = Service_Write_single_ValueDataSource(server, session, (const UA_VariableNode*)orig, wvalue);
  603. UA_NodeStore_release(orig);
  604. return retval;
  605. }
  606. UA_NodeStore_release(orig);
  607. }
  608. return UA_Server_editNode(server, session, &wvalue->nodeId,
  609. (UA_EditNodeCallback)MoveAttributeIntoNode, wvalue);
  610. }
  611. void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
  612. UA_WriteResponse *response) {
  613. UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
  614. UA_LOG_DEBUG(server->logger, UA_LOGCATEGORY_SESSION,
  615. "Processing WriteRequest for Session (ns=%i,i=%i)",
  616. session->sessionId.namespaceIndex, session->sessionId.identifier.numeric);
  617. if(request->nodesToWriteSize <= 0) {
  618. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  619. return;
  620. }
  621. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_STATUSCODE], request->nodesToWriteSize);
  622. if(!response->results) {
  623. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  624. return;
  625. }
  626. #ifdef UA_EXTERNAL_NAMESPACES
  627. UA_Boolean isExternal[request->nodesToWriteSize];
  628. UA_UInt32 indices[request->nodesToWriteSize];
  629. UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
  630. for(size_t j = 0; j < server->externalNamespacesSize; j++) {
  631. UA_UInt32 indexSize = 0;
  632. for(UA_Int32 i = 0; i < request->nodesToWriteSize; i++) {
  633. if(request->nodesToWrite[i].nodeId.namespaceIndex !=
  634. server->externalNamespaces[j].index)
  635. continue;
  636. isExternal[i] = UA_TRUE;
  637. indices[indexSize] = i;
  638. indexSize++;
  639. }
  640. if(indexSize == 0)
  641. continue;
  642. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  643. ens->writeNodes(ens->ensHandle, &request->requestHeader, request->nodesToWrite,
  644. indices, indexSize, response->results, response->diagnosticInfos);
  645. }
  646. #endif
  647. response->resultsSize = request->nodesToWriteSize;
  648. for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
  649. #ifdef UA_EXTERNAL_NAMESPACES
  650. if(!isExternal[i])
  651. #endif
  652. response->results[i] = Service_Write_single(server, session, &request->nodesToWrite[i]);
  653. }
  654. }