|
@@ -23,14 +23,16 @@ void UA_MonitoredItem_init(UA_MonitoredItem *mon, UA_Subscription *sub) {
|
|
|
}
|
|
|
|
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
|
|
|
-static UA_StatusCode removeMonitoredItemFromNodeCallback(UA_Server *server, UA_Session *session, UA_Node *node,
|
|
|
- void *data) {
|
|
|
+static UA_StatusCode
|
|
|
+removeMonitoredItemFromNodeCallback(UA_Server *server, UA_Session *session,
|
|
|
+ UA_Node *node, void *data) {
|
|
|
/* data is the monitoredItemID */
|
|
|
/* catch edge case that it's the first element */
|
|
|
if (data == ((UA_ObjectNode *) node)->monitoredItemQueue) {
|
|
|
((UA_ObjectNode *)node)->monitoredItemQueue = ((UA_MonitoredItem *)data)->next;
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
+
|
|
|
/* SLIST_FOREACH */
|
|
|
for (UA_MonitoredItem *entry = ((UA_ObjectNode *) node)->monitoredItemQueue->next;
|
|
|
entry != NULL; entry=entry->next) {
|
|
@@ -71,15 +73,17 @@ void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem)
|
|
|
}
|
|
|
monitoredItem->queueSize = 0;
|
|
|
}
|
|
|
+
|
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
|
|
|
- if (monitoredItem->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
|
|
|
+ if(monitoredItem->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
|
|
|
/* Remove the monitored item from the node queue */
|
|
|
- UA_Server_editNode(server, NULL, &monitoredItem->monitoredNodeId, removeMonitoredItemFromNodeCallback,
|
|
|
- monitoredItem);
|
|
|
+ UA_Server_editNode(server, NULL, &monitoredItem->monitoredNodeId,
|
|
|
+ removeMonitoredItemFromNodeCallback, monitoredItem);
|
|
|
/* Delete the event filter */
|
|
|
UA_EventFilter_deleteMembers(&monitoredItem->filter.eventFilter);
|
|
|
}
|
|
|
#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
|
|
|
+
|
|
|
/* Remove the monitored item */
|
|
|
UA_String_deleteMembers(&monitoredItem->indexRange);
|
|
|
UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
|
|
@@ -88,7 +92,8 @@ void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem)
|
|
|
UA_Server_delayedFree(server, monitoredItem);
|
|
|
}
|
|
|
|
|
|
-UA_StatusCode MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) {
|
|
|
+UA_StatusCode
|
|
|
+MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) {
|
|
|
if(mon->queueSize <= mon->maxQueueSize)
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
|
|
@@ -126,13 +131,14 @@ UA_StatusCode MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem
|
|
|
/* Remove the notification from the queues */
|
|
|
TAILQ_REMOVE(&mon->queue, del, listEntry);
|
|
|
TAILQ_REMOVE(&sub->notificationQueue, del, globalEntry);
|
|
|
+
|
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
|
|
|
/* TODO: provide additional protection for overflowEvents according to specification */
|
|
|
/* removing an overflowEvent should not reduce the queueSize */
|
|
|
UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE);
|
|
|
- if (!(del->data.event.fields.eventFieldsSize == 1
|
|
|
- && del->data.event.fields.eventFields->type == &UA_TYPES[UA_TYPES_NODEID]
|
|
|
- && UA_NodeId_equal((UA_NodeId *)del->data.event.fields.eventFields->data, &overflowId))) {
|
|
|
+ if(!(del->data.event.fields.eventFieldsSize == 1 &&
|
|
|
+ del->data.event.fields.eventFields->type == &UA_TYPES[UA_TYPES_NODEID] &&
|
|
|
+ UA_NodeId_equal((UA_NodeId *)del->data.event.fields.eventFields->data, &overflowId))) {
|
|
|
--mon->queueSize;
|
|
|
--sub->notificationQueueSize;
|
|
|
}
|
|
@@ -141,46 +147,47 @@ UA_StatusCode MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem
|
|
|
--sub->notificationQueueSize;
|
|
|
#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
|
|
|
|
|
|
- /* Free the notification */
|
|
|
- if (mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
|
|
|
+ /* Create an overflow notification */
|
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
|
|
|
+ if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
|
|
|
/* EventFilterResult currently isn't being used
|
|
|
UA_EventFilterResult_deleteMembers(&del->data.event->result); */
|
|
|
UA_EventFieldList_deleteMembers(&del->data.event.fields);
|
|
|
|
|
|
/* cause an overflowEvent */
|
|
|
- /* an overflowEvent does not care about event filters and as such will not be "triggered" correctly.
|
|
|
- * Instead, a notification will be inserted into the queue which includes only the nodeId of the
|
|
|
- * overflowEventType. It is up to the client to check for possible overflows.
|
|
|
- */
|
|
|
+ /* an overflowEvent does not care about event filters and as such
|
|
|
+ * will not be "triggered" correctly. Instead, a notification will
|
|
|
+ * be 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 = (UA_Notification *) UA_malloc(sizeof(UA_Notification));
|
|
|
- if (!overflowNotification) {
|
|
|
+ if(!overflowNotification)
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
- }
|
|
|
|
|
|
UA_EventFieldList_init(&overflowNotification->data.event.fields);
|
|
|
-
|
|
|
overflowNotification->data.event.fields.eventFields = UA_Variant_new();
|
|
|
- if (!overflowNotification->data.event.fields.eventFields) {
|
|
|
+ if(!overflowNotification->data.event.fields.eventFields) {
|
|
|
UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields);
|
|
|
UA_free(overflowNotification);
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
}
|
|
|
- UA_Variant_init(overflowNotification->data.event.fields.eventFields);
|
|
|
|
|
|
+ UA_Variant_init(overflowNotification->data.event.fields.eventFields);
|
|
|
overflowNotification->data.event.fields.eventFieldsSize = 1;
|
|
|
UA_Variant_setScalarCopy(overflowNotification->data.event.fields.eventFields,
|
|
|
- &overflowId, &UA_TYPES[UA_TYPES_NODEID]);
|
|
|
+ &overflowId, &UA_TYPES[UA_TYPES_NODEID]);
|
|
|
overflowNotification->mon = mon;
|
|
|
- if (mon->discardOldest) {
|
|
|
+ if(mon->discardOldest) {
|
|
|
TAILQ_INSERT_HEAD(&mon->queue, overflowNotification, listEntry);
|
|
|
TAILQ_INSERT_HEAD(&mon->subscription->notificationQueue, overflowNotification, globalEntry);
|
|
|
} else {
|
|
|
TAILQ_INSERT_TAIL(&mon->queue, overflowNotification, listEntry);
|
|
|
TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, overflowNotification, globalEntry);
|
|
|
}
|
|
|
-#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
|
|
|
}
|
|
|
+#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
|
|
|
+
|
|
|
+ /* Free the notification */
|
|
|
UA_Notification_delete(del);
|
|
|
}
|
|
|
|
|
@@ -212,58 +219,61 @@ UA_StatusCode MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem
|
|
|
#define ABS_SUBTRACT_TYPE_INDEPENDENT(a,b) ((a)>(b)?(a)-(b):(b)-(a))
|
|
|
|
|
|
static UA_INLINE UA_Boolean
|
|
|
-outOfDeadBand(const void *data1, const void *data2, const size_t index, const UA_DataType *type, const UA_Double deadbandValue) {
|
|
|
- if (type == &UA_TYPES[UA_TYPES_SBYTE]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_SByte*)data1)[index], ((const UA_SByte*)data2)[index]) <= deadbandValue)
|
|
|
+outOfDeadBand(const void *data1, const void *data2, const size_t index,
|
|
|
+ const UA_DataType *type, const UA_Double deadbandValue) {
|
|
|
+ if(type == &UA_TYPES[UA_TYPES_SBYTE]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_SByte*)data1)[index],
|
|
|
+ ((const UA_SByte*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_BYTE]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Byte*)data1)[index], ((const UA_Byte*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_BYTE]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Byte*)data1)[index],
|
|
|
+ ((const UA_Byte*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_INT16]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int16*)data1)[index], ((const UA_Int16*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_INT16]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int16*)data1)[index],
|
|
|
+ ((const UA_Int16*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_UINT16]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt16*)data1)[index], ((const UA_UInt16*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_UINT16]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt16*)data1)[index],
|
|
|
+ ((const UA_UInt16*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_INT32]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int32*)data1)[index], ((const UA_Int32*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_INT32]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int32*)data1)[index],
|
|
|
+ ((const UA_Int32*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_UINT32]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt32*)data1)[index], ((const UA_UInt32*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_UINT32]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt32*)data1)[index],
|
|
|
+ ((const UA_UInt32*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_INT64]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int64*)data1)[index], ((const UA_Int64*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_INT64]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int64*)data1)[index],
|
|
|
+ ((const UA_Int64*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_UINT64]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt64*)data1)[index], ((const UA_UInt64*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_UINT64]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt64*)data1)[index],
|
|
|
+ ((const UA_UInt64*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_FLOAT]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Float*)data1)[index], ((const UA_Float*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_FLOAT]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Float*)data1)[index],
|
|
|
+ ((const UA_Float*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
- } else
|
|
|
- if (type == &UA_TYPES[UA_TYPES_DOUBLE]) {
|
|
|
- if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Double*)data1)[index], ((const UA_Double*)data2)[index]) <= deadbandValue)
|
|
|
+ } else if(type == &UA_TYPES[UA_TYPES_DOUBLE]) {
|
|
|
+ if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Double*)data1)[index],
|
|
|
+ ((const UA_Double*)data2)[index]) <= deadbandValue)
|
|
|
return false;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
static UA_INLINE UA_Boolean
|
|
|
-updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue, const UA_Double deadbandValue) {
|
|
|
- if (value->arrayLength != oldValue->arrayLength) {
|
|
|
+updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue,
|
|
|
+ const UA_Double deadbandValue) {
|
|
|
+ if(value->arrayLength != oldValue->arrayLength)
|
|
|
return true;
|
|
|
- }
|
|
|
- if (value->type != oldValue->type) {
|
|
|
+
|
|
|
+ if(value->type != oldValue->type)
|
|
|
return true;
|
|
|
- }
|
|
|
+
|
|
|
if (UA_Variant_isScalar(value)) {
|
|
|
return outOfDeadBand(value->data, oldValue->data, 0, value->type, deadbandValue);
|
|
|
} else {
|
|
@@ -291,7 +301,8 @@ detectValueChangeWithFilter(UA_Server *server, UA_MonitoredItem *mon, UA_DataVal
|
|
|
(mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
|
|
|
mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) {
|
|
|
if(mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE) {
|
|
|
- if(!updateNeededForFilteredValue(&value->value, &mon->lastValue, mon->filter.dataChangeFilter.deadbandValue))
|
|
|
+ if(!updateNeededForFilteredValue(&value->value, &mon->lastValue,
|
|
|
+ mon->filter.dataChangeFilter.deadbandValue))
|
|
|
return false;
|
|
|
}
|
|
|
/* else if (mon->filter.deadbandType == UA_DEADBANDTYPE_PERCENT) {
|
|
@@ -327,6 +338,7 @@ detectValueChangeWithFilter(UA_Server *server, UA_MonitoredItem *mon, UA_DataVal
|
|
|
&bufPos, &bufEnd, NULL, NULL);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
|
UA_LOG_WARNING_SESSION(server->config.logger, session,
|
|
|
"Subscription %u | MonitoredItem %i | "
|