ua_services_attribute.c 25 KB

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