ua_subscription_monitoreditem.c 15 KB

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