ua_subscription_monitoreditem.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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-2018 (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. #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
  14. /****************/
  15. /* Notification */
  16. /****************/
  17. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  18. static const UA_NodeId overflowEventType =
  19. {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_EVENTQUEUEOVERFLOWEVENTTYPE}};
  20. static const UA_NodeId simpleOverflowEventType =
  21. {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE}};
  22. static UA_Boolean
  23. UA_Notification_isOverflowEvent(UA_Server *server, UA_Notification *n) {
  24. UA_MonitoredItem *mon = n->mon;
  25. if(mon->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER)
  26. return false;
  27. UA_EventFieldList *efl = &n->data.event.fields;
  28. if(efl->eventFieldsSize >= 1 &&
  29. efl->eventFields[0].type == &UA_TYPES[UA_TYPES_NODEID] &&
  30. isNodeInTree(server->nsCtx, (const UA_NodeId *)efl->eventFields[0].data,
  31. &overflowEventType, &subtypeId, 1)) {
  32. return true;
  33. }
  34. return false;
  35. }
  36. /* The specification states in Part 4 5.12.1.5 that an EventQueueOverflowEvent
  37. * "is generated when the first Event has to be discarded [...] without
  38. * discarding any other event". So only generate one for all deleted events. */
  39. static UA_StatusCode
  40. createEventOverflowNotification(UA_Server *server, UA_Subscription *sub,
  41. UA_MonitoredItem *mon, UA_Notification *indicator) {
  42. /* Avoid two redundant overflow events in a row */
  43. if(UA_Notification_isOverflowEvent(server, indicator))
  44. return UA_STATUSCODE_GOOD;
  45. /* A notification is inserted into the queue which includes only the
  46. * NodeId of the overflowEventType. It is up to the client to check for
  47. * possible overflows. */
  48. /* Allocate the notification */
  49. UA_Notification *overflowNotification = (UA_Notification *)
  50. UA_malloc(sizeof(UA_Notification));
  51. if(!overflowNotification)
  52. return UA_STATUSCODE_BADOUTOFMEMORY;;
  53. /* Set the notification fields */
  54. overflowNotification->mon = mon;
  55. UA_EventFieldList_init(&overflowNotification->data.event.fields);
  56. overflowNotification->data.event.fields.eventFields = UA_Variant_new();
  57. if(!overflowNotification->data.event.fields.eventFields) {
  58. UA_free(overflowNotification);
  59. return UA_STATUSCODE_BADOUTOFMEMORY;;
  60. }
  61. overflowNotification->data.event.fields.eventFieldsSize = 1;
  62. UA_StatusCode retval =
  63. UA_Variant_setScalarCopy(overflowNotification->data.event.fields.eventFields,
  64. &simpleOverflowEventType, &UA_TYPES[UA_TYPES_NODEID]);
  65. if(retval != UA_STATUSCODE_GOOD) {
  66. UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields);
  67. UA_free(overflowNotification);
  68. return retval;
  69. }
  70. /* Insert before the "indicator notification". This is either first in
  71. * the queue (if the oldest notification was removed) or before the new
  72. * event that remains the last element of the queue. */
  73. TAILQ_INSERT_BEFORE(indicator, overflowNotification, listEntry);
  74. TAILQ_INSERT_BEFORE(indicator, overflowNotification, globalEntry);
  75. ++mon->eventOverflows;
  76. ++mon->queueSize;
  77. ++sub->notificationQueueSize;
  78. ++sub->eventNotifications;
  79. return UA_STATUSCODE_GOOD;
  80. }
  81. #endif
  82. void
  83. UA_Notification_enqueue(UA_Server *server, UA_Subscription *sub,
  84. UA_MonitoredItem *mon, UA_Notification *n) {
  85. /* Add to the MonitoredItem */
  86. TAILQ_INSERT_TAIL(&mon->queue, n, listEntry);
  87. ++mon->queueSize;
  88. /* Add to the subscription */
  89. TAILQ_INSERT_TAIL(&sub->notificationQueue, n, globalEntry);
  90. ++sub->notificationQueueSize;
  91. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  92. if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
  93. ++sub->eventNotifications;
  94. if(UA_Notification_isOverflowEvent(server, n))
  95. ++mon->eventOverflows;
  96. } else
  97. #endif
  98. {
  99. ++sub->dataChangeNotifications;
  100. }
  101. /* Ensure enough space is available in the MonitoredItem. Do this only after
  102. * adding the new Notification. */
  103. UA_MonitoredItem_ensureQueueSpace(server, mon);
  104. }
  105. void
  106. UA_Notification_dequeue(UA_Server *server, UA_Notification *n) {
  107. UA_MonitoredItem *mon = n->mon;
  108. UA_Subscription *sub = mon->subscription;
  109. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  110. if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
  111. --sub->eventNotifications;
  112. if(UA_Notification_isOverflowEvent(server, n))
  113. --mon->eventOverflows;
  114. } else
  115. #endif
  116. {
  117. --sub->dataChangeNotifications;
  118. }
  119. TAILQ_REMOVE(&mon->queue, n, listEntry);
  120. --mon->queueSize;
  121. TAILQ_REMOVE(&sub->notificationQueue, n, globalEntry);
  122. --sub->notificationQueueSize;
  123. }
  124. void
  125. UA_Notification_delete(UA_Notification *n) {
  126. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  127. UA_MonitoredItem *mon = n->mon;
  128. if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
  129. UA_EventFieldList_deleteMembers(&n->data.event.fields);
  130. /* EventFilterResult currently isn't being used
  131. * UA_EventFilterResult_delete(notification->data.event->result); */
  132. } else
  133. #endif
  134. {
  135. UA_DataValue_deleteMembers(&n->data.value);
  136. }
  137. UA_free(n);
  138. }
  139. /*****************/
  140. /* MonitoredItem */
  141. /*****************/
  142. void
  143. UA_MonitoredItem_init(UA_MonitoredItem *mon, UA_Subscription *sub) {
  144. memset(mon, 0, sizeof(UA_MonitoredItem));
  145. mon->subscription = sub;
  146. TAILQ_INIT(&mon->queue);
  147. }
  148. void
  149. UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
  150. /* Remove the sampling callback */
  151. UA_MonitoredItem_unregisterSampleCallback(server, monitoredItem);
  152. /* Remove the queued notifications if attached to a subscription (not a
  153. * local MonitoredItem) */
  154. if(monitoredItem->subscription) {
  155. UA_Notification *notification, *notification_tmp;
  156. TAILQ_FOREACH_SAFE(notification, &monitoredItem->queue,
  157. listEntry, notification_tmp) {
  158. /* Remove the item from the queues and free the memory */
  159. UA_Notification_dequeue(server, notification);
  160. UA_Notification_delete(notification);
  161. }
  162. }
  163. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  164. if(monitoredItem->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
  165. /* Remove the monitored item from the node queue */
  166. UA_Server_editNode(server, NULL, &monitoredItem->monitoredNodeId,
  167. UA_MonitoredItem_removeNodeEventCallback, monitoredItem);
  168. UA_EventFilter_clear(&monitoredItem->filter.eventFilter);
  169. } else
  170. #endif
  171. {
  172. /* UA_DataChangeFilter does not hold dynamic content we need to free */
  173. /* UA_DataChangeFilter_clear(&monitoredItem->filter.dataChangeFilter); */
  174. }
  175. /* Deregister MonitoredItem in userland */
  176. if(server->config.monitoredItemRegisterCallback && monitoredItem->registered) {
  177. /* Get the session context. Local MonitoredItems don't have a subscription. */
  178. UA_Session *session = NULL;
  179. if(monitoredItem->subscription)
  180. session = monitoredItem->subscription->session;
  181. if(!session)
  182. session = &server->adminSession;
  183. /* Get the node context */
  184. void *targetContext = NULL;
  185. UA_Server_getNodeContext(server, monitoredItem->monitoredNodeId, &targetContext);
  186. /* Deregister */
  187. server->config.monitoredItemRegisterCallback(server, &session->sessionId,
  188. session->sessionHandle,
  189. &monitoredItem->monitoredNodeId,
  190. targetContext, monitoredItem->attributeId, true);
  191. }
  192. /* Remove the monitored item */
  193. if(monitoredItem->listEntry.le_prev != NULL)
  194. LIST_REMOVE(monitoredItem, listEntry);
  195. UA_String_deleteMembers(&monitoredItem->indexRange);
  196. UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
  197. UA_Variant_deleteMembers(&monitoredItem->lastValue);
  198. UA_NodeId_deleteMembers(&monitoredItem->monitoredNodeId);
  199. /* No actual callback, just remove the structure */
  200. monitoredItem->delayedFreePointers.callback = NULL;
  201. UA_WorkQueue_enqueueDelayed(&server->workQueue, &monitoredItem->delayedFreePointers);
  202. }
  203. UA_StatusCode
  204. UA_MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) {
  205. /* Assert: The eventoverflow are counted in the queue size; There can be
  206. * only one eventoverflow more than normal entries */
  207. UA_assert(mon->queueSize >= mon->eventOverflows);
  208. UA_assert(mon->eventOverflows <= mon->queueSize - mon->eventOverflows + 1);
  209. /* Nothing to do */
  210. if(mon->queueSize - mon->eventOverflows <= mon->maxQueueSize)
  211. return UA_STATUSCODE_GOOD;
  212. #ifdef __clang_analyzer__
  213. return UA_STATUSCODE_GOOD;
  214. #endif
  215. /* Remove notifications until the queue size is reached */
  216. UA_Subscription *sub = mon->subscription;
  217. while(mon->queueSize - mon->eventOverflows > mon->maxQueueSize) {
  218. /* At least two notifications that are not eventOverflows in the queue */
  219. UA_assert(mon->queueSize - mon->eventOverflows >= 2);
  220. /* Select the next notification to delete. Skip over overflow events. */
  221. UA_Notification *del = NULL;
  222. if(mon->discardOldest) {
  223. /* Remove the oldest */
  224. del = TAILQ_FIRST(&mon->queue);
  225. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  226. while(UA_Notification_isOverflowEvent(server, del))
  227. del = TAILQ_NEXT(del, listEntry); /* skip overflow events */
  228. #endif
  229. } else {
  230. /* Remove the second newest (to keep the up-to-date notification).
  231. * The last entry is not an OverflowEvent -- we just added it. */
  232. del = TAILQ_LAST(&mon->queue, NotificationQueue);
  233. del = TAILQ_PREV(del, NotificationQueue, listEntry);
  234. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  235. while(UA_Notification_isOverflowEvent(server, del))
  236. del = TAILQ_PREV(del, NotificationQueue, listEntry); /* skip overflow events */
  237. #endif
  238. }
  239. UA_assert(del); /* There must have been one entry that can be deleted */
  240. /* Move the entry after del in the per-MonitoredItem queue right after
  241. * del in the global queue. (It is already right after del in the
  242. * per-MonitoredItem queue.) This is required so we don't starve
  243. * MonitoredItems with a high sampling interval by always removing their
  244. * first appearance in the gloal queue for the Subscription. */
  245. UA_Notification *after_del = TAILQ_NEXT(del, listEntry);
  246. UA_assert(after_del); /* There must be one remaining element after del */
  247. TAILQ_REMOVE(&sub->notificationQueue, after_del, globalEntry);
  248. TAILQ_INSERT_AFTER(&sub->notificationQueue, del, after_del, globalEntry);
  249. /* Delete the notification */
  250. UA_Notification_dequeue(server, del);
  251. UA_Notification_delete(del);
  252. }
  253. /* Get the element where the overflow shall be announced (infobits or
  254. * overflowevent) */
  255. UA_Notification *indicator;
  256. if(mon->discardOldest)
  257. indicator = TAILQ_FIRST(&mon->queue);
  258. else
  259. indicator = TAILQ_LAST(&mon->queue, NotificationQueue);
  260. UA_assert(indicator);
  261. /* Create an overflow notification */
  262. #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
  263. if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
  264. return createEventOverflowNotification(server, sub, mon, indicator);
  265. } else
  266. #endif
  267. {
  268. /* Set the infobits of a datachange notification */
  269. if(mon->maxQueueSize > 1) {
  270. /* Add the infobits either to the newest or the new last entry */
  271. indicator->data.value.hasStatus = true;
  272. indicator->data.value.status |=
  273. (UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);
  274. }
  275. }
  276. return UA_STATUSCODE_GOOD;
  277. }
  278. UA_StatusCode
  279. UA_MonitoredItem_registerSampleCallback(UA_Server *server, UA_MonitoredItem *mon) {
  280. if(mon->sampleCallbackIsRegistered)
  281. return UA_STATUSCODE_GOOD;
  282. /* Only DataChange MonitoredItems have a callback with a sampling interval */
  283. if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER)
  284. return UA_STATUSCODE_GOOD;
  285. UA_StatusCode retval =
  286. UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_MonitoredItem_sampleCallback,
  287. mon, mon->samplingInterval, &mon->sampleCallbackId);
  288. if(retval == UA_STATUSCODE_GOOD)
  289. mon->sampleCallbackIsRegistered = true;
  290. return retval;
  291. }
  292. void
  293. UA_MonitoredItem_unregisterSampleCallback(UA_Server *server, UA_MonitoredItem *mon) {
  294. if(!mon->sampleCallbackIsRegistered)
  295. return;
  296. UA_Server_removeRepeatedCallback(server, mon->sampleCallbackId);
  297. mon->sampleCallbackIsRegistered = false;
  298. }
  299. #endif /* UA_ENABLE_SUBSCRIPTIONS */