Kaynağa Gözat

Server: Remove UA_MonitoredItemType and directly check the AttributeId

Julius Pfrommer 5 yıl önce
ebeveyn
işleme
aed211f46e

+ 53 - 52
src/server/ua_services_subscription.c

@@ -37,7 +37,6 @@ setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription,
     Subscription_unregisterPublishCallback(server, subscription);
 
     /* re-parameterize the subscription */
-    subscription->publishingInterval = requestedPublishingInterval;
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.publishingIntervalLimits,
                                requestedPublishingInterval, subscription->publishingInterval);
     /* check for nan*/
@@ -171,41 +170,53 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
                          UA_MonitoringMode monitoringMode,
                          const UA_MonitoringParameters *params,
                          const UA_DataType* dataType) {
-    /* Filter */
-    if (mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER &&
-            params->filter.encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) {
-        return UA_STATUSCODE_BADEVENTFILTERINVALID;
-    }
-
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
-    if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED) {
-        UA_DataChangeFilter_init(&(mon->filter.dataChangeFilter));
-        mon->filter.dataChangeFilter.trigger = UA_DATACHANGETRIGGER_STATUSVALUE;
-    } else if(params->filter.content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) {
-        UA_DataChangeFilter *filter = (UA_DataChangeFilter *)params->filter.content.decoded.data;
-        // TODO implement EURange to support UA_DEADBANDTYPE_PERCENT
-        switch(filter->deadbandType) {
-        case UA_DEADBANDTYPE_NONE:
-            break;
-        case UA_DEADBANDTYPE_ABSOLUTE:
-            if(!dataType || !UA_DataType_isNumeric(dataType))
-                return UA_STATUSCODE_BADFILTERNOTALLOWED;
-            break;
-        case UA_DEADBANDTYPE_PERCENT:
-            return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
-        default:
-            return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
-        }
-        UA_DataChangeFilter_copy(filter, &mon->filter.dataChangeFilter);
-#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-    } else if (params->filter.content.decoded.type == &UA_TYPES[UA_TYPES_EVENTFILTER]) {
-        UA_EventFilter_copy((UA_EventFilter *)params->filter.content.decoded.data,
-                            &mon->filter.eventFilter);
+    if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
+        /* Event MonitoredItem */
+#ifndef UA_ENABLE_SUBSCRIPTIONS_EVENTS
+        return UA_STATUSCODE_BADNOTSUPPORTED;
+#else
+        if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED &&
+           params->filter.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE)
+            return UA_STATUSCODE_BADEVENTFILTERINVALID;
+        if(params->filter.content.decoded.type != &UA_TYPES[UA_TYPES_EVENTFILTER])
+            return UA_STATUSCODE_BADEVENTFILTERINVALID;
+        UA_EventFilter_clear(&mon->filter.eventFilter);
+        retval = UA_EventFilter_copy((UA_EventFilter *)params->filter.content.decoded.data,
+                                     &mon->filter.eventFilter);
 #endif
     } else {
-        return UA_STATUSCODE_BADMONITOREDITEMFILTERINVALID;
+        /* DataChange MonitoredItem */
+        if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED &&
+           params->filter.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) {
+            /* Default: Look for status and value */
+            UA_DataChangeFilter_clear(&mon->filter.dataChangeFilter);
+            mon->filter.dataChangeFilter.trigger = UA_DATACHANGETRIGGER_STATUSVALUE;
+        } else if(params->filter.content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) {
+            UA_DataChangeFilter *filter = (UA_DataChangeFilter *)params->filter.content.decoded.data;
+            // TODO implement EURange to support UA_DEADBANDTYPE_PERCENT
+            switch(filter->deadbandType) {
+            case UA_DEADBANDTYPE_NONE:
+                break;
+            case UA_DEADBANDTYPE_ABSOLUTE:
+                if(!dataType || !UA_DataType_isNumeric(dataType))
+                    return UA_STATUSCODE_BADFILTERNOTALLOWED;
+                break;
+            case UA_DEADBANDTYPE_PERCENT:
+                return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
+            default:
+                return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
+            }
+            retval = UA_DataChangeFilter_copy(filter, &mon->filter.dataChangeFilter);
+        } else {
+            return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
+        }
     }
 
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
     /* <-- The point of no return --> */
 
     /* Unregister the callback */
@@ -220,8 +231,8 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 
     /* SamplingInterval */
     UA_Double samplingInterval = params->samplingInterval;
+
     if(mon->attributeId == UA_ATTRIBUTEID_VALUE) {
-        mon->monitoredItemType = UA_MONITOREDITEMTYPE_CHANGENOTIFY;
         const UA_VariableNode *vn = (const UA_VariableNode *)
             UA_Nodestore_getNode(server->nsCtx, &mon->monitoredNodeId);
         if(vn) {
@@ -230,21 +241,13 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
                 samplingInterval = vn->minimumSamplingInterval;
             UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node *)vn);
         }
-    } else if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
-        /* TODO: events should not need a samplinginterval */
-        samplingInterval = 10000.0f; // 10 seconds to reduce the load
-        mon->monitoredItemType = UA_MONITOREDITEMTYPE_EVENTNOTIFY;
-    } else {
-        mon->monitoredItemType = UA_MONITOREDITEMTYPE_CHANGENOTIFY;
     }
-    mon->samplingInterval = samplingInterval;
+
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.samplingIntervalLimits,
                                samplingInterval, mon->samplingInterval);
     if(samplingInterval != samplingInterval) /* Check for nan */
         mon->samplingInterval = server->config.samplingIntervalLimits.min;
 
-    UA_assert(mon->monitoredItemType != 0);
-
     /* QueueSize */
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.queueSizeLimits,
                                params->queueSize, mon->maxQueueSize);
@@ -263,8 +266,9 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 static const UA_String binaryEncoding = {sizeof("Default Binary") - 1, (UA_Byte *)"Default Binary"};
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-static UA_StatusCode UA_Server_addMonitoredItemToNodeEditNodeCallback(UA_Server *server, UA_Session *session,
-                                                                      UA_Node *node, void *data) {
+static UA_StatusCode
+UA_Server_addMonitoredItemToNodeEditNodeCallback(UA_Server *server, UA_Session *session,
+                                                 UA_Node *node, void *data) {
     /* data is the MonitoredItem */
     /* SLIST_INSERT_HEAD */
     ((UA_MonitoredItem *)data)->next = ((UA_ObjectNode *)node)->monitoredItemQueue;
@@ -367,7 +371,7 @@ Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session, struct cre
         newMon->monitoredItemId = ++cmc->sub->lastMonitoredItemId;
         UA_Subscription_addMonitoredItem(server, cmc->sub, newMon);
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-        if(newMon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
+        if(newMon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
             /* Insert the monitored item into the node's queue */
             UA_Server_editNode(server, NULL, &newMon->monitoredNodeId,
                                UA_Server_addMonitoredItemToNodeEditNodeCallback, newMon);
@@ -400,7 +404,8 @@ Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session, struct cre
                         newMon->monitoredItemId);
 
     /* Create the first sample */
-    if(request->monitoringMode == UA_MONITORINGMODE_REPORTING)
+    if(request->monitoringMode == UA_MONITORINGMODE_REPORTING &&
+       newMon->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER)
         UA_MonitoredItem_sampleCallback(server, newMon);
 
     /* Prepare the response */
@@ -547,29 +552,25 @@ Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
         return;
     }
 
-    if(mon->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY
-           && mon->monitoredItemType != UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
-        *result = UA_STATUSCODE_BADNOTIMPLEMENTED;
-        return;
-    }
-
     /* Check if the MonitoringMode is valid or not */
     if(smc->monitoringMode > UA_MONITORINGMODE_REPORTING) {
         *result = UA_STATUSCODE_BADMONITORINGMODEINVALID;
         return;
     }
 
+    /* Nothing has changed */
     if(mon->monitoringMode == smc->monitoringMode)
         return;
 
     mon->monitoringMode = smc->monitoringMode;
+
     if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING) {
         *result = UA_MonitoredItem_registerSampleCallback(server, mon);
     } else {
         UA_MonitoredItem_unregisterSampleCallback(server, mon);
 
         // TODO correctly implement SAMPLING
-        /*  Setting the mode to DISABLED or SAMPLING causes all queued Notifications to be deleted */
+        /* Setting the mode to DISABLED or SAMPLING causes all queued Notifications to be deleted */
         UA_Notification *notification, *notification_tmp;
         TAILQ_FOREACH_SAFE(notification, &mon->queue, listEntry, notification_tmp) {
             UA_Notification_dequeue(server, notification);

+ 11 - 17
src/server/ua_subscription.c

@@ -282,20 +282,9 @@ prepareNotificationMessage(UA_Server *server, UA_Subscription *sub,
         UA_Notification_dequeue(server, notification);
 
         /* Move the content to the response */
-        if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-            UA_assert(dcn != NULL); /* Have at least one change notification */
-            /* Move the content to the response */
-            UA_MonitoredItemNotification *min = &dcn->monitoredItems[dcnPos];
-            min->clientHandle = mon->clientHandle;
-            min->value = notification->data.value;
-            UA_DataValue_init(&notification->data.value); /* Reset after the value has been moved */
-            dcnPos++;
-        }
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-        else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY && scn) {
-            // TODO: Handling of StatusChangeNotifications
-            scn = NULL; /* At most one per PublishReponse */
-        } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY && enl) {
+        if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
+
             UA_assert(enl != NULL); /* Have at least one event notification */
 
             /* Move the content to the response */
@@ -305,11 +294,16 @@ prepareNotificationMessage(UA_Server *server, UA_Subscription *sub,
             efl->clientHandle = mon->clientHandle;
 
             enlPos++;
-        }
+        } else
 #endif
-        else {
-            UA_Notification_delete(notification);
-            continue; /* Unknown type. Nothing to do */
+        {
+            UA_assert(dcn != NULL); /* Have at least one change notification */
+            /* Move the content to the response */
+            UA_MonitoredItemNotification *min = &dcn->monitoredItems[dcnPos];
+            min->clientHandle = mon->clientHandle;
+            min->value = notification->data.value;
+            UA_DataValue_init(&notification->data.value); /* Reset after the value has been moved */
+            dcnPos++;
         }
 
         UA_Notification_delete(notification);

+ 9 - 15
src/server/ua_subscription.h

@@ -42,12 +42,6 @@ _UA_BEGIN_DECLS
 /* MonitoredItem */
 /*****************/
 
-typedef enum {
-    UA_MONITOREDITEMTYPE_CHANGENOTIFY = 1,
-    UA_MONITOREDITEMTYPE_STATUSNOTIFY = 2,
-    UA_MONITOREDITEMTYPE_EVENTNOTIFY = 4
-} UA_MonitoredItemType;
-
 struct UA_MonitoredItem;
 typedef struct UA_MonitoredItem UA_MonitoredItem;
 
@@ -91,31 +85,28 @@ typedef TAILQ_HEAD(NotificationQueue, UA_Notification) NotificationQueue;
 struct UA_MonitoredItem {
     UA_DelayedCallback delayedFreePointers;
     LIST_ENTRY(UA_MonitoredItem) listEntry;
-    UA_Subscription *subscription;
+    UA_Subscription *subscription; /* Local MonitoredItem if the subscription is NULL */
     UA_UInt32 monitoredItemId;
     UA_UInt32 clientHandle;
     UA_Boolean registered; /* Was the MonitoredItem registered in Userland with
                               the callback? */
 
     /* Settings */
-    UA_MonitoredItemType monitoredItemType;
     UA_TimestampsToReturn timestampsToReturn;
     UA_MonitoringMode monitoringMode;
     UA_NodeId monitoredNodeId;
     UA_UInt32 attributeId;
     UA_String indexRange;
     UA_Double samplingInterval; // [ms]
-    UA_UInt32 maxQueueSize; /* The max number of enqueued notifications (not
-                               counting overflow events) */
     UA_Boolean discardOldest;
-    // TODO: dataEncoding is hardcoded to UA binary
     union {
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-        UA_EventFilter eventFilter;
+        UA_EventFilter eventFilter; /* If attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER */
 #endif
         UA_DataChangeFilter dataChangeFilter;
     } filter;
     UA_Variant lastValue;
+    // TODO: dataEncoding is hardcoded to UA binary
 
     /* Sample Callback */
     UA_UInt64 sampleCallbackId;
@@ -124,9 +115,12 @@ struct UA_MonitoredItem {
 
     /* Notification Queue */
     NotificationQueue queue;
+    UA_UInt32 maxQueueSize; /* The max number of enqueued notifications (not
+                             * counting overflow events) */
     UA_UInt32 queueSize;
-     /* Save the amount of OverflowEvents in a separate counter */
-     UA_UInt32 eventOverflows;
+    UA_UInt32 eventOverflows; /* Separate counter for the queue. Can at most
+                               * double the queue size */
+
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
     UA_MonitoredItem *next;
 #endif
@@ -195,7 +189,7 @@ struct UA_Subscription {
 
     /* MonitoredItems */
     UA_UInt32 lastMonitoredItemId; /* increase the identifiers */
-    LIST_HEAD(UA_ListOfUAMonitoredItems, UA_MonitoredItem) monitoredItems;
+    LIST_HEAD(, UA_MonitoredItem) monitoredItems;
     UA_UInt32 monitoredItemsSize;
 
     /* Global list of notifications from the MonitoredItems */

+ 2 - 7
src/server/ua_subscription_datachange.c

@@ -296,7 +296,7 @@ static UA_StatusCode
 sampleCallbackWithValue(UA_Server *server, UA_Session *session,
                         UA_Subscription *sub, UA_MonitoredItem *mon,
                         UA_DataValue *value, UA_Boolean *movedValue) {
-    UA_assert(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY);
+    UA_assert(mon->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER);
 
     /* Contains heap-allocated binary encoding of the value if a change was detected */
     UA_ByteString binValueEncoding = UA_BYTESTRING_NULL;
@@ -401,12 +401,7 @@ UA_MonitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredIt
                          "MonitoredItem %i | Sample callback called",
                          sub ? sub->subscriptionId : 0, monitoredItem->monitoredItemId);
 
-    if(monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | "
-                             "MonitoredItem %i | Not a data change notification",
-                             sub ? sub->subscriptionId : 0, monitoredItem->monitoredItemId);
-        return;
-    }
+    UA_assert(monitoredItem->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER);
 
     /* Get the node */
     const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, &monitoredItem->monitoredNodeId);

+ 88 - 126
src/server/ua_subscription_monitoreditem.c

@@ -28,11 +28,11 @@ static const UA_NodeId simpleOverflowEventType =
 static UA_Boolean
 UA_Notification_isOverflowEvent(UA_Server *server, UA_Notification *n) {
     UA_MonitoredItem *mon = n->mon;
-    if(mon->monitoredItemType != UA_MONITOREDITEMTYPE_EVENTNOTIFY)
+    if(mon->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER)
         return false;
 
     UA_EventFieldList *efl = &n->data.event.fields;
-    if(efl->eventFieldsSize == 1 &&
+    if(efl->eventFieldsSize >= 1 &&
        efl->eventFields[0].type == &UA_TYPES[UA_TYPES_NODEID] &&
        isNodeInTree(server->nsCtx, (const UA_NodeId *)efl->eventFields[0].data,
                     &overflowEventType, &subtypeId, 1)) {
@@ -42,21 +42,34 @@ UA_Notification_isOverflowEvent(UA_Server *server, UA_Notification *n) {
     return false;
 }
 
-static UA_Notification *
-createEventOverflowNotification(UA_MonitoredItem *mon) {
-    UA_Notification *overflowNotification = (UA_Notification *) UA_malloc(sizeof(UA_Notification));
+/* The specification states in Part 4 5.12.1.5 that an EventQueueOverflowEvent
+ * "is generated when the first Event has to be discarded [...] without
+ * discarding any other event". So only generate one for all deleted events. */
+static UA_StatusCode
+createEventOverflowNotification(UA_Server *server, UA_Subscription *sub,
+                                UA_MonitoredItem *mon, UA_Notification *indicator) {
+    /* Avoid two redundant overflow events in a row */
+    if(UA_Notification_isOverflowEvent(server, indicator))
+        return UA_STATUSCODE_GOOD;
+
+    /* A notification is inserted into the queue which includes only the
+     * NodeId of the overflowEventType. It is up to the client to check for
+     * possible overflows. */
+
+    /* Allocate the notification */
+    UA_Notification *overflowNotification = (UA_Notification *)
+        UA_malloc(sizeof(UA_Notification));
     if(!overflowNotification)
-        return NULL;
+        return UA_STATUSCODE_BADOUTOFMEMORY;;
 
+    /* Set the notification fields */
     overflowNotification->mon = mon;
     UA_EventFieldList_init(&overflowNotification->data.event.fields);
     overflowNotification->data.event.fields.eventFields = UA_Variant_new();
     if(!overflowNotification->data.event.fields.eventFields) {
-        UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields);
         UA_free(overflowNotification);
-        return NULL;
+        return UA_STATUSCODE_BADOUTOFMEMORY;;
     }
-
     overflowNotification->data.event.fields.eventFieldsSize = 1;
     UA_StatusCode retval =
         UA_Variant_setScalarCopy(overflowNotification->data.event.fields.eventFields,
@@ -64,10 +77,19 @@ createEventOverflowNotification(UA_MonitoredItem *mon) {
     if(retval != UA_STATUSCODE_GOOD) {
         UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields);
         UA_free(overflowNotification);
-        return NULL;
+        return retval;
     }
 
-    return overflowNotification;
+    /* Insert before the "indicator notification". This is either first in
+     * the queue (if the oldest notification was removed) or before the new
+     * event that remains the last element of the queue. */
+    TAILQ_INSERT_BEFORE(indicator, overflowNotification, listEntry);
+    TAILQ_INSERT_BEFORE(indicator, overflowNotification, globalEntry);
+    ++mon->eventOverflows;
+    ++mon->queueSize;
+    ++sub->notificationQueueSize;
+    ++sub->eventNotifications;
+    return UA_STATUSCODE_GOOD;
 }
 
 #endif
@@ -83,16 +105,15 @@ UA_Notification_enqueue(UA_Server *server, UA_Subscription *sub,
     TAILQ_INSERT_TAIL(&sub->notificationQueue, n, globalEntry);
     ++sub->notificationQueueSize;
 
-    if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        ++sub->dataChangeNotifications;
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
+    if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
         ++sub->eventNotifications;
         if(UA_Notification_isOverflowEvent(server, n))
             ++mon->eventOverflows;
+    } else
 #endif
-    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY) {
-        ++sub->statusChangeNotifications;
+    {
+        ++sub->dataChangeNotifications;
     }
 
     /* Ensure enough space is available in the MonitoredItem. Do this only after
@@ -105,16 +126,15 @@ UA_Notification_dequeue(UA_Server *server, UA_Notification *n) {
     UA_MonitoredItem *mon = n->mon;
     UA_Subscription *sub = mon->subscription;
 
-    if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        --sub->dataChangeNotifications;
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
+    if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
         --sub->eventNotifications;
         if(UA_Notification_isOverflowEvent(server, n))
             --mon->eventOverflows;
+    } else
 #endif
-    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY) {
-        --sub->statusChangeNotifications;
+    {
+        --sub->dataChangeNotifications;
     }
 
     TAILQ_REMOVE(&mon->queue, n, listEntry);
@@ -126,20 +146,17 @@ UA_Notification_dequeue(UA_Server *server, UA_Notification *n) {
 
 void
 UA_Notification_delete(UA_Notification *n) {
-    UA_MonitoredItem *mon = n->mon;
-
-    if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        UA_DataValue_deleteMembers(&n->data.value);
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
+    UA_MonitoredItem *mon = n->mon;
+    if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
         UA_EventFieldList_deleteMembers(&n->data.event.fields);
         /* EventFilterResult currently isn't being used
          * UA_EventFilterResult_delete(notification->data.event->result); */
+    } else
 #endif
-    } else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY) {
-        /* Nothing to do */
+    {
+        UA_DataValue_deleteMembers(&n->data.value);
     }
-
     UA_free(n);
 }
 
@@ -156,15 +173,8 @@ UA_MonitoredItem_init(UA_MonitoredItem *mon, UA_Subscription *sub) {
 
 void
 UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
-    if(monitoredItem->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        /* Remove the sampling callback */
-        UA_MonitoredItem_unregisterSampleCallback(server, monitoredItem);
-    } else if (monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
-        /* TODO: Access val data.event */
-        UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
-                     "MonitoredItemTypes other than ChangeNotify or EventNotify "
-                     "are not supported yet");
-    }
+    /* Remove the sampling callback */
+    UA_MonitoredItem_unregisterSampleCallback(server, monitoredItem);
 
     /* Remove the queued notifications if attached to a subscription (not a
      * local MonitoredItem) */
@@ -178,19 +188,17 @@ UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
         }
     }
 
-    /* if(monitoredItem->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY)
-     * -> UA_DataChangeFilter does not hold dynamic content we need to free */
-
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-    if(monitoredItem->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
+    if(monitoredItem->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
         /* Remove the monitored item from the node queue */
         UA_Server_editNode(server, NULL, &monitoredItem->monitoredNodeId,
                            UA_MonitoredItem_removeNodeEventCallback, monitoredItem);
         UA_EventFilter_clear(&monitoredItem->filter.eventFilter);
     } else
-#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
+#endif
     {
-        UA_DataChangeFilter_clear(&monitoredItem->filter.dataChangeFilter);
+        /* UA_DataChangeFilter does not hold dynamic content we need to free */
+        /* UA_DataChangeFilter_clear(&monitoredItem->filter.dataChangeFilter); */
     }
 
     /* Deregister MonitoredItem in userland */
@@ -208,7 +216,8 @@ UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
 
         /* Deregister */
         server->config.monitoredItemRegisterCallback(server, &session->sessionId,
-                                                     session->sessionHandle, &monitoredItem->monitoredNodeId,
+                                                     session->sessionHandle,
+                                                     &monitoredItem->monitoredNodeId,
                                                      targetContext, monitoredItem->attributeId, true);
     }
 
@@ -225,70 +234,58 @@ UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
     UA_WorkQueue_enqueueDelayed(&server->workQueue, &monitoredItem->delayedFreePointers);
 }
 
-#ifdef __clang_analyzer__
-# define UA_CA_assert(clause) UA_assert(clause)
-#else
-# define UA_CA_assert(clause)
-#endif
-
 UA_StatusCode
 UA_MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) {
+    /* Assert: The eventoverflow are counted in the queue size; There can be
+     * only one eventoverflow more than normal entries */
+    UA_assert(mon->queueSize >= mon->eventOverflows);
+    UA_assert(mon->eventOverflows <= mon->queueSize - mon->eventOverflows + 1);
+
+    /* Nothing to do */
     if(mon->queueSize - mon->eventOverflows <= mon->maxQueueSize)
         return UA_STATUSCODE_GOOD;
 
-    /* Remove notifications until the queue size is reached */
-    UA_Subscription *sub = mon->subscription;
 #ifdef __clang_analyzer__
-    UA_Notification *last = NULL;
+    return UA_STATUSCODE_GOOD;
 #endif
-
+    
+    /* Remove notifications until the queue size is reached */
+    UA_Subscription *sub = mon->subscription;
     while(mon->queueSize - mon->eventOverflows > mon->maxQueueSize) {
         /* At least two notifications that are not eventOverflows in the queue */
         UA_assert(mon->queueSize - mon->eventOverflows >= 2);
 
         /* Select the next notification to delete. Skip over overflow events. */
-        UA_Notification *del;
+        UA_Notification *del = NULL;
         if(mon->discardOldest) {
             /* Remove the oldest */
             del = TAILQ_FIRST(&mon->queue);
-            UA_CA_assert(del != last);
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-            while(UA_Notification_isOverflowEvent(server, del)) {
+            while(UA_Notification_isOverflowEvent(server, del))
                 del = TAILQ_NEXT(del, listEntry); /* skip overflow events */
-                UA_CA_assert(del != last);
-            }
 #endif
         } else {
-            /* Remove the second newest (to keep the up-to-date notification) */
+            /* Remove the second newest (to keep the up-to-date notification).
+             * The last entry is not an OverflowEvent -- we just added it. */
             del = TAILQ_LAST(&mon->queue, NotificationQueue);
             del = TAILQ_PREV(del, NotificationQueue, listEntry);
-            UA_CA_assert(del != last);
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-            while(UA_Notification_isOverflowEvent(server, del)) {
+            while(UA_Notification_isOverflowEvent(server, del))
                 del = TAILQ_PREV(del, NotificationQueue, listEntry); /* skip overflow events */
-                UA_CA_assert(del != last);
-            }
 #endif
         }
 
-        // NOLINTNEXTLINE
-        UA_assert(del && del->mon == mon);
+        UA_assert(del); /* There must have been one entry that can be deleted */
 
-        /* Move after_del right after del in the global queue. (It is already
-         * right after del in the per-MonitoredItem queue.) This is required so
-         * we don't starve MonitoredItems with a high sampling interval by
-         * always removing their first appearance in the gloal queue for the
-         * Subscription. */
+        /* Move the entry after del in the per-MonitoredItem queue right after
+         * del in the global queue. (It is already right after del in the
+         * per-MonitoredItem queue.) This is required so we don't starve
+         * MonitoredItems with a high sampling interval by always removing their
+         * first appearance in the gloal queue for the Subscription. */
         UA_Notification *after_del = TAILQ_NEXT(del, listEntry);
-        UA_CA_assert(after_del != last);
-        if(after_del) {
-            TAILQ_REMOVE(&sub->notificationQueue, after_del, globalEntry);
-            TAILQ_INSERT_AFTER(&sub->notificationQueue, del, after_del, globalEntry);
-        }
-
-#ifdef __clang_analyzer__
-        last = del;
-#endif
+        UA_assert(after_del); /* There must be one remaining element after del */
+        TAILQ_REMOVE(&sub->notificationQueue, after_del, globalEntry);
+        TAILQ_INSERT_AFTER(&sub->notificationQueue, del, after_del, globalEntry);
 
         /* Delete the notification */
         UA_Notification_dequeue(server, del);
@@ -303,57 +300,22 @@ UA_MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) {
     else
         indicator = TAILQ_LAST(&mon->queue, NotificationQueue);
     UA_assert(indicator);
-    UA_CA_assert(indicator != last);
 
     /* Create an overflow notification */
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
-    /* The specification states in Part 4 5.12.1.5 that an EventQueueOverflowEvent
-     * "is generated when the first Event has to be discarded [...] without discarding
-     * any other event". So only generate one for all deleted events. */
-    if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
-        /* Avoid two redundant overflow events in a row */
-        if(UA_Notification_isOverflowEvent(server, indicator)) {
-            if(mon->discardOldest)
-                return UA_STATUSCODE_GOOD;
-            UA_Notification *prev = TAILQ_PREV(indicator, NotificationQueue, listEntry);
-            UA_CA_assert(prev != last);
-            if(prev && UA_Notification_isOverflowEvent(server, prev))
-                return UA_STATUSCODE_GOOD;
-        }
-
-        /* A notification is inserted into the queue which includes only the
-         * NodeId of the overflowEventType. It is up to the client to check for
-         * possible overflows. */
-        UA_Notification *overflowNotification = createEventOverflowNotification(mon);
-        if(!overflowNotification)
-            return UA_STATUSCODE_BADOUTOFMEMORY;
-
-        /* Insert before the "indicator notification". This is either first in
-         * the queue (if the oldest notification was removed) or before the new
-         * event that remains the last element of the queue. */
-        TAILQ_INSERT_BEFORE(indicator, overflowNotification, listEntry);
-        TAILQ_INSERT_BEFORE(indicator, overflowNotification, globalEntry);
-        ++mon->eventOverflows;
-        ++mon->queueSize;
-        ++sub->notificationQueueSize;
-        ++sub->eventNotifications;
-    }
-#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
-
-    /* Set the infobits of a datachange notification */
-    if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        /* Set the infobits */
+    if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
+        return createEventOverflowNotification(server, sub, mon, indicator);
+    } else
+#endif
+    {
+        /* Set the infobits of a datachange notification */
         if(mon->maxQueueSize > 1) {
             /* Add the infobits either to the newest or the new last entry */
-            indicator->data.value.hasStatus = true;  // NOLINT
-            indicator->data.value.status |= (UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);
-        } else {
-            /* If the queue size is reduced to one, remove the infobits */
-            indicator->data.value.status &=  // NOLINT
-                ~(UA_StatusCode)(UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);  // NOLINT
+            indicator->data.value.hasStatus = true;
+            indicator->data.value.status |=
+                (UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW);
         }
     }
-
     return UA_STATUSCODE_GOOD;
 }
 
@@ -363,7 +325,7 @@ UA_MonitoredItem_registerSampleCallback(UA_Server *server, UA_MonitoredItem *mon
         return UA_STATUSCODE_GOOD;
 
     /* Only DataChange MonitoredItems have a callback with a sampling interval */
-    if(mon->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY)
+    if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER)
         return UA_STATUSCODE_GOOD;
 
     UA_StatusCode retval =