ua_subscription_datachange.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  6. * Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  7. * Copyright 2018 (c) Ari Breitkreuz, fortiss GmbH
  8. * Copyright 2018 (c) Thomas Stalder, Blue Time Concept SA
  9. * Copyright 2018 (c) Fabian Arndt, Root-Core
  10. */
  11. #include "ua_server_internal.h"
  12. #include "ua_subscription.h"
  13. #include "ua_types_encoding_binary.h"
  14. #ifdef UA_ENABLE_DA
  15. #include <math.h> // fabs
  16. #endif
  17. #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
  18. #define UA_VALUENCODING_MAXSTACK 512
  19. #define ABS_SUBTRACT_TYPE_INDEPENDENT(a,b) ((a)>(b)?(a)-(b):(b)-(a))
  20. static UA_Boolean
  21. outOfDeadBand(const void *data1, const void *data2, const size_t arrayPos,
  22. const UA_DataType *type, const UA_Double deadbandValue) {
  23. if(type == &UA_TYPES[UA_TYPES_BOOLEAN]) {
  24. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Boolean*)data1)[arrayPos],
  25. ((const UA_Boolean*)data2)[arrayPos]) <= deadbandValue)
  26. return false;
  27. } else if(type == &UA_TYPES[UA_TYPES_SBYTE]) {
  28. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_SByte*)data1)[arrayPos],
  29. ((const UA_SByte*)data2)[arrayPos]) <= deadbandValue)
  30. return false;
  31. } else if(type == &UA_TYPES[UA_TYPES_BYTE]) {
  32. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Byte*)data1)[arrayPos],
  33. ((const UA_Byte*)data2)[arrayPos]) <= deadbandValue)
  34. return false;
  35. } else if(type == &UA_TYPES[UA_TYPES_INT16]) {
  36. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int16*)data1)[arrayPos],
  37. ((const UA_Int16*)data2)[arrayPos]) <= deadbandValue)
  38. return false;
  39. } else if(type == &UA_TYPES[UA_TYPES_UINT16]) {
  40. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt16*)data1)[arrayPos],
  41. ((const UA_UInt16*)data2)[arrayPos]) <= deadbandValue)
  42. return false;
  43. } else if(type == &UA_TYPES[UA_TYPES_INT32]) {
  44. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int32*)data1)[arrayPos],
  45. ((const UA_Int32*)data2)[arrayPos]) <= deadbandValue)
  46. return false;
  47. } else if(type == &UA_TYPES[UA_TYPES_UINT32]) {
  48. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt32*)data1)[arrayPos],
  49. ((const UA_UInt32*)data2)[arrayPos]) <= deadbandValue)
  50. return false;
  51. } else if(type == &UA_TYPES[UA_TYPES_INT64]) {
  52. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int64*)data1)[arrayPos],
  53. ((const UA_Int64*)data2)[arrayPos]) <= deadbandValue)
  54. return false;
  55. } else if(type == &UA_TYPES[UA_TYPES_UINT64]) {
  56. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt64*)data1)[arrayPos],
  57. ((const UA_UInt64*)data2)[arrayPos]) <= deadbandValue)
  58. return false;
  59. } else if(type == &UA_TYPES[UA_TYPES_FLOAT]) {
  60. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Float*)data1)[arrayPos],
  61. ((const UA_Float*)data2)[arrayPos]) <= deadbandValue)
  62. return false;
  63. } else if(type == &UA_TYPES[UA_TYPES_DOUBLE]) {
  64. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Double*)data1)[arrayPos],
  65. ((const UA_Double*)data2)[arrayPos]) <= deadbandValue)
  66. return false;
  67. }
  68. return true;
  69. }
  70. #ifdef UA_ENABLE_DA
  71. static UA_INLINE UA_Boolean
  72. outOfPercentDeadBand(const void *data1, const void *data2, const size_t index,
  73. const UA_DataType *type, const UA_Double deadbandValue, UA_Range* range) {
  74. if(type == &UA_TYPES[UA_TYPES_SBYTE]) {
  75. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_SByte*)data1)[index],
  76. ((const UA_SByte*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  77. *(const UA_SByte*)data1 > range->high)
  78. return false;
  79. } else if(type == &UA_TYPES[UA_TYPES_BYTE]) {
  80. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Byte*)data1)[index],
  81. ((const UA_Byte*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  82. *(const UA_Byte*)data1 > range->high)
  83. return false;
  84. } else if(type == &UA_TYPES[UA_TYPES_INT16]) {
  85. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int16*)data1)[index],
  86. ((const UA_Int16*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  87. *(const UA_Int16*)data1 > range->high)
  88. return false;
  89. } else if(type == &UA_TYPES[UA_TYPES_UINT16]) {
  90. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt16*)data1)[index],
  91. ((const UA_UInt16*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  92. *(const UA_UInt16*)data1 > range->high)
  93. return false;
  94. } else if(type == &UA_TYPES[UA_TYPES_INT32]) {
  95. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int32*)data1)[index],
  96. ((const UA_Int32*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  97. *(const UA_Int32*)data1 > range->high)
  98. return false;
  99. } else if(type == &UA_TYPES[UA_TYPES_UINT32]) {
  100. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt32*)data1)[index],
  101. ((const UA_UInt32*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  102. *(const UA_UInt32*)data1 > range->high)
  103. return false;
  104. } else if(type == &UA_TYPES[UA_TYPES_INT64]) {
  105. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int64*)data1)[index],
  106. ((const UA_Int64*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  107. *(const UA_Int64*)data1 > range->high)
  108. return false;
  109. } else if(type == &UA_TYPES[UA_TYPES_UINT64]) {
  110. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt64*)data1)[index],
  111. ((const UA_UInt64*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  112. *(const UA_UInt64*)data1 > range->high)
  113. return false;
  114. } else if(type == &UA_TYPES[UA_TYPES_FLOAT]) {
  115. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Float*)data1)[index],
  116. ((const UA_Float*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  117. *(const UA_Float*)data1 > range->high)
  118. return false;
  119. } else if(type == &UA_TYPES[UA_TYPES_DOUBLE]) {
  120. if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Double*)data1)[index],
  121. ((const UA_Double*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
  122. *(const UA_Double*)data1 > range->high)
  123. return false;
  124. }
  125. return true;
  126. }
  127. #endif /* UA_ENABLE_DA */
  128. static UA_INLINE UA_Boolean
  129. updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue,
  130. const UA_Double deadbandValue) {
  131. if(value->arrayLength != oldValue->arrayLength)
  132. return true;
  133. if(value->type != oldValue->type)
  134. return true;
  135. if (UA_Variant_isScalar(value)) {
  136. return outOfDeadBand(value->data, oldValue->data, 0, value->type, deadbandValue);
  137. } else {
  138. for (size_t i = 0; i < value->arrayLength; ++i) {
  139. if (outOfDeadBand(value->data, oldValue->data, i, value->type, deadbandValue))
  140. return true;
  141. }
  142. }
  143. return false;
  144. }
  145. #ifdef UA_ENABLE_DA
  146. static UA_INLINE UA_Boolean
  147. updateNeededForFilteredPercentValue(const UA_Variant *value, const UA_Variant *oldValue,
  148. const UA_Double deadbandValue, UA_Range* euRange) {
  149. if(value->arrayLength != oldValue->arrayLength)
  150. return true;
  151. if(value->type != oldValue->type)
  152. return true;
  153. if (UA_Variant_isScalar(value)) {
  154. return outOfPercentDeadBand(value->data, oldValue->data, 0, value->type, deadbandValue, euRange);
  155. } else {
  156. for (size_t i = 0; i < value->arrayLength; ++i) {
  157. if (outOfPercentDeadBand(value->data, oldValue->data, i, value->type, deadbandValue, euRange))
  158. return true;
  159. }
  160. }
  161. return false;
  162. }
  163. static UA_Boolean
  164. updateNeededForStatusCode(const UA_DataValue *value, const UA_MonitoredItem *mon) {
  165. if (UA_Variant_isScalar(&value->value)) {
  166. if(value->status != mon->lastStatus)
  167. return true;
  168. }
  169. return false;
  170. }
  171. #endif
  172. /* When a change is detected, encoding contains the heap-allocated binary encoded value */
  173. static UA_Boolean
  174. detectValueChangeWithFilter(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon,
  175. UA_DataValue *value, UA_ByteString *encoding) {
  176. #ifdef UA_ENABLE_DA
  177. if(mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS) {
  178. if(!updateNeededForStatusCode(value, mon))
  179. return false;
  180. }
  181. #endif
  182. if(UA_DataType_isNumeric(value->value.type) &&
  183. (mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
  184. mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) {
  185. if(mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE) {
  186. if(!updateNeededForFilteredValue(&value->value, &mon->lastValue,
  187. mon->filter.dataChangeFilter.deadbandValue))
  188. return false;
  189. }
  190. #ifdef UA_ENABLE_DA
  191. else if(mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_PERCENT) {
  192. UA_QualifiedName qn = UA_QUALIFIEDNAME(0, "EURange");
  193. UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, mon->monitoredNodeId, 1, &qn);
  194. if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { //if branch is not entried, property has been found
  195. UA_BrowsePathResult_deleteMembers(&bpr);
  196. return false;
  197. }
  198. const UA_VariableNode* node = (const UA_VariableNode*) UA_Nodestore_get(server, &bpr.targets->targetId.nodeId);
  199. UA_Range* euRange = (UA_Range*) node->value.data.value.value.data;
  200. if(!updateNeededForFilteredPercentValue(&value->value, &mon->lastValue,
  201. mon->filter.dataChangeFilter.deadbandValue, euRange)) {
  202. if(!updateNeededForStatusCode(value, mon)) //when same value, but different status code is written
  203. return false;
  204. }
  205. }
  206. #endif
  207. }
  208. /* Stack-allocate some memory for the value encoding. We might heap-allocate
  209. * more memory if needed. This is just enough for scalars and small
  210. * structures. */
  211. UA_STACKARRAY(UA_Byte, stackValueEncoding, UA_VALUENCODING_MAXSTACK);
  212. UA_ByteString valueEncoding;
  213. valueEncoding.data = stackValueEncoding;
  214. valueEncoding.length = UA_VALUENCODING_MAXSTACK;
  215. /* Encode the value */
  216. UA_Byte *bufPos = valueEncoding.data;
  217. const UA_Byte *bufEnd = &valueEncoding.data[valueEncoding.length];
  218. UA_StatusCode retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE],
  219. &bufPos, &bufEnd, NULL, NULL);
  220. if(retval == UA_STATUSCODE_BADENCODINGERROR) {
  221. size_t binsize = UA_calcSizeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE]);
  222. if(binsize == 0)
  223. return false;
  224. if(binsize > UA_VALUENCODING_MAXSTACK) {
  225. retval = UA_ByteString_allocBuffer(&valueEncoding, binsize);
  226. if(retval == UA_STATUSCODE_GOOD) {
  227. bufPos = valueEncoding.data;
  228. bufEnd = &valueEncoding.data[valueEncoding.length];
  229. retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE],
  230. &bufPos, &bufEnd, NULL, NULL);
  231. }
  232. }
  233. }
  234. if(retval != UA_STATUSCODE_GOOD) {
  235. UA_LOG_WARNING_SESSION(&server->config.logger, sub ? sub->session : &server->adminSession,
  236. "Subscription %u | MonitoredItem %i | "
  237. "Could not encode the value the MonitoredItem with status %s",
  238. sub ? sub->subscriptionId : 0, mon->monitoredItemId,
  239. UA_StatusCode_name(retval));
  240. return false;
  241. }
  242. /* Has the value changed? */
  243. valueEncoding.length = (uintptr_t)bufPos - (uintptr_t)valueEncoding.data;
  244. UA_Boolean changed = (!mon->lastSampledValue.data ||
  245. !UA_String_equal(&valueEncoding, &mon->lastSampledValue));
  246. /* No change */
  247. if(!changed) {
  248. if(valueEncoding.data != stackValueEncoding)
  249. UA_ByteString_deleteMembers(&valueEncoding);
  250. return false;
  251. }
  252. /* Change detected. Copy encoding on the heap if necessary. */
  253. if(valueEncoding.data == stackValueEncoding) {
  254. retval = UA_ByteString_copy(&valueEncoding, encoding);
  255. if(retval != UA_STATUSCODE_GOOD) {
  256. UA_LOG_WARNING_SESSION(&server->config.logger, sub ? sub->session : &server->adminSession,
  257. "Subscription %u | MonitoredItem %i | "
  258. "Detected change, but could not allocate memory for the notification"
  259. "with status %s", sub ? sub->subscriptionId : 0,
  260. mon->monitoredItemId, UA_StatusCode_name(retval));
  261. return false;
  262. }
  263. return true;
  264. }
  265. *encoding = valueEncoding;
  266. return true;
  267. }
  268. /* Has this sample changed from the last one? The method may allocate additional
  269. * space for the encoding buffer. Detect the change in encoding->data. */
  270. static UA_Boolean
  271. detectValueChange(UA_Server *server, UA_MonitoredItem *mon,
  272. UA_DataValue value, UA_ByteString *encoding) {
  273. /* Apply Filter */
  274. if(mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS)
  275. value.hasValue = false;
  276. value.hasServerTimestamp = false;
  277. value.hasServerPicoseconds = false;
  278. if(mon->filter.dataChangeFilter.trigger < UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
  279. value.hasSourceTimestamp = false;
  280. value.hasSourcePicoseconds = false;
  281. }
  282. /* Detect the value change */
  283. return detectValueChangeWithFilter(server, mon->subscription, mon, &value, encoding);
  284. }
  285. /* Returns whether the sample was stored in the MonitoredItem */
  286. static UA_Boolean
  287. sampleCallbackWithValue(UA_Server *server, UA_MonitoredItem *monitoredItem,
  288. UA_DataValue *value) {
  289. UA_assert(monitoredItem->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY);
  290. UA_Subscription *sub = monitoredItem->subscription;
  291. /* Contains heap-allocated binary encoding of the value if a change was detected */
  292. UA_ByteString binValueEncoding = UA_BYTESTRING_NULL;
  293. /* Has the value changed? Allocates memory in binValueEncoding if necessary.
  294. * value is edited internally so we make a shallow copy. */
  295. UA_Boolean changed = detectValueChange(server, monitoredItem, *value, &binValueEncoding);
  296. if(!changed)
  297. return false;
  298. UA_Boolean storedValue = false;
  299. if(sub) {
  300. /* Allocate a new notification */
  301. UA_Notification *newNotification = (UA_Notification *)UA_malloc(sizeof(UA_Notification));
  302. if(!newNotification) {
  303. UA_LOG_WARNING_SESSION(&server->config.logger, sub ? sub->session : &server->adminSession,
  304. "Subscription %u | MonitoredItem %i | "
  305. "Item for the publishing queue could not be allocated",
  306. sub->subscriptionId, monitoredItem->monitoredItemId);
  307. UA_ByteString_deleteMembers(&binValueEncoding);
  308. return false;
  309. }
  310. if(monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS){
  311. if(value->value.storageType == UA_VARIANT_DATA) {
  312. newNotification->data.value.hasStatus = true;
  313. UA_StatusCode_copy(&value->status, &newNotification->data.value.status);
  314. storedValue = true;
  315. }
  316. else { /* => (value->value.storageType == UA_VARIANT_DATA_NODELETE) */
  317. UA_StatusCode retval = UA_DataValue_copy(value, &newNotification->data.value);
  318. if(retval != UA_STATUSCODE_GOOD) {
  319. UA_ByteString_deleteMembers(&binValueEncoding);
  320. UA_free(newNotification);
  321. return false;
  322. }
  323. }
  324. }
  325. else if(monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
  326. monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
  327. if(value->value.storageType == UA_VARIANT_DATA) {
  328. newNotification->data.value = *value; /* Move the value to the notification */
  329. storedValue = true;
  330. } else { /* => (value->value.storageType == UA_VARIANT_DATA_NODELETE) */
  331. UA_StatusCode retval = UA_DataValue_copy(value, &newNotification->data.value);
  332. if(retval != UA_STATUSCODE_GOOD) {
  333. UA_ByteString_deleteMembers(&binValueEncoding);
  334. UA_free(newNotification);
  335. return false;
  336. }
  337. }
  338. }
  339. /* <-- Point of no return --> */
  340. /* Enqueue the new notification */
  341. newNotification->mon = monitoredItem;
  342. UA_Notification_enqueue(server, sub, monitoredItem, newNotification);
  343. } else {
  344. /* Call the local callback if not attached to a subscription */
  345. UA_LocalMonitoredItem *localMon = (UA_LocalMonitoredItem*) monitoredItem;
  346. void *nodeContext = NULL;
  347. UA_Server_getNodeContext(server, monitoredItem->monitoredNodeId, &nodeContext);
  348. localMon->callback.dataChangeCallback(server, monitoredItem->monitoredItemId,
  349. localMon->context,
  350. &monitoredItem->monitoredNodeId,
  351. nodeContext, monitoredItem->attributeId,
  352. value);
  353. }
  354. // If someone called UA_Server_deleteMonitoredItem in the user callback,
  355. // then the monitored item will be deleted soon. So, there is no need to
  356. // add the lastValue or lastSampledValue to it.
  357. //
  358. // If we do so, we will leak
  359. // the memory of that values, because UA_Server_deleteMonitoredItem
  360. // already deleted all members and scheduled the monitored item pointer
  361. // for later delete. In the later delete the monitored item will be deleted
  362. // and not the members.
  363. //
  364. // Also in the later delete, all type information is lost and a deleteMember
  365. // is not possible.
  366. //
  367. // We do detect if the monitored item is already defunct.
  368. if (!monitoredItem->sampleCallbackIsRegistered) {
  369. UA_ByteString_deleteMembers(&binValueEncoding);
  370. return storedValue;
  371. }
  372. /* Store the encoding for comparison */
  373. UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
  374. monitoredItem->lastSampledValue = binValueEncoding;
  375. /* Store the value for filter comparison (we don't want to decode
  376. * lastSampledValue in every iteration) */
  377. if((monitoredItem->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_NONE ||
  378. monitoredItem->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE ||
  379. monitoredItem->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_PERCENT) &&
  380. (monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS ||
  381. monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
  382. monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) {
  383. UA_Variant_deleteMembers(&monitoredItem->lastValue);
  384. UA_Variant_copy(&value->value, &monitoredItem->lastValue);
  385. #ifdef UA_ENABLE_DA
  386. UA_StatusCode_deleteMembers(&monitoredItem->lastStatus);
  387. UA_StatusCode_copy(&value->status, &monitoredItem->lastStatus);
  388. #endif
  389. /* Don't test the return code here. If this fails, lastValue is empty
  390. * and a notification will be forced for the next deadband comparison. */
  391. }
  392. return storedValue;
  393. }
  394. void
  395. UA_MonitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) {
  396. UA_Subscription *sub = monitoredItem->subscription;
  397. UA_Session *session = &server->adminSession;
  398. if(sub)
  399. session = sub->session;
  400. if(monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
  401. UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | "
  402. "MonitoredItem %i | Not a data change notification",
  403. sub ? sub->subscriptionId : 0, monitoredItem->monitoredItemId);
  404. return;
  405. }
  406. /* Get the node */
  407. const UA_Node *node = UA_Nodestore_get(server, &monitoredItem->monitoredNodeId);
  408. /* Sample the value. The sample can still point into the node. */
  409. UA_DataValue value;
  410. UA_DataValue_init(&value);
  411. if(node) {
  412. UA_ReadValueId rvid;
  413. UA_ReadValueId_init(&rvid);
  414. rvid.nodeId = monitoredItem->monitoredNodeId;
  415. rvid.attributeId = monitoredItem->attributeId;
  416. rvid.indexRange = monitoredItem->indexRange;
  417. ReadWithNode(node, server, session, monitoredItem->timestampsToReturn, &rvid, &value);
  418. } else {
  419. value.hasStatus = true;
  420. value.status = UA_STATUSCODE_BADNODEIDUNKNOWN;
  421. }
  422. /* Operate on the sample */
  423. UA_Boolean storedValue = sampleCallbackWithValue(server, monitoredItem, &value);
  424. /* Delete the sample if it was not stored in the MonitoredItem */
  425. if(!storedValue)
  426. UA_DataValue_deleteMembers(&value); /* Does nothing for UA_VARIANT_DATA_NODELETE */
  427. if(node)
  428. UA_Nodestore_release(server, node);
  429. }
  430. #endif /* UA_ENABLE_SUBSCRIPTIONS */