|
@@ -30,7 +30,7 @@ void UA_Subscription_deleteMembers(UA_Subscription *subscription, UA_Server *ser
|
|
|
UA_MonitoredItem *mon, *tmp_mon;
|
|
|
LIST_FOREACH_SAFE(mon, &subscription->MonitoredItems, listEntry, tmp_mon) {
|
|
|
LIST_REMOVE(mon, listEntry);
|
|
|
- MonitoredItem_delete(mon);
|
|
|
+ MonitoredItem_delete(server, mon);
|
|
|
}
|
|
|
|
|
|
// Delete unpublished Notifications
|
|
@@ -242,33 +242,29 @@ UA_StatusCode Subscription_unregisterUpdateJob(UA_Server *server, UA_Subscriptio
|
|
|
/*****************/
|
|
|
|
|
|
UA_MonitoredItem * UA_MonitoredItem_new() {
|
|
|
- UA_MonitoredItem *new = (UA_MonitoredItem *) UA_malloc(sizeof(UA_MonitoredItem));
|
|
|
- new->queueSize = (UA_BoundedUInt32) { .min = 0, .max = 0, .current = 0};
|
|
|
- new->lastSampled = 0;
|
|
|
+ UA_MonitoredItem *new = UA_malloc(sizeof(UA_MonitoredItem));
|
|
|
+ new->queueSize = (UA_BoundedUInt32) { .min = 0, .max = 0, .current = 0};
|
|
|
// FIXME: This is currently hardcoded;
|
|
|
new->monitoredItemType = MONITOREDITEM_TYPE_CHANGENOTIFY;
|
|
|
TAILQ_INIT(&new->queue);
|
|
|
UA_NodeId_init(&new->monitoredNodeId);
|
|
|
- new->lastSampledValue.data = 0;
|
|
|
+ new->lastSampledValue = UA_BYTESTRING_NULL;
|
|
|
+ memset(&new->sampleJobGuid, 0, sizeof(UA_Guid));
|
|
|
+ new->sampleJobIsRegistered = false;
|
|
|
return new;
|
|
|
}
|
|
|
|
|
|
-void MonitoredItem_delete(UA_MonitoredItem *monitoredItem) {
|
|
|
- // Delete Queued Data
|
|
|
+void MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
|
|
|
+ MonitoredItem_unregisterUpdateJob(server, monitoredItem);
|
|
|
MonitoredItem_ClearQueue(monitoredItem);
|
|
|
- // Remove from subscription list
|
|
|
LIST_REMOVE(monitoredItem, listEntry);
|
|
|
- // Release comparison sample
|
|
|
- if(monitoredItem->lastSampledValue.data != NULL) {
|
|
|
- UA_free(monitoredItem->lastSampledValue.data);
|
|
|
- }
|
|
|
-
|
|
|
- UA_NodeId_deleteMembers(&(monitoredItem->monitoredNodeId));
|
|
|
+ UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
|
|
|
+ UA_NodeId_deleteMembers(&monitoredItem->monitoredNodeId);
|
|
|
UA_free(monitoredItem);
|
|
|
}
|
|
|
|
|
|
UA_UInt32 MonitoredItem_QueueToDataChangeNotifications(UA_MonitoredItemNotification *dst,
|
|
|
- UA_MonitoredItem *monitoredItem) {
|
|
|
+ UA_MonitoredItem *monitoredItem) {
|
|
|
UA_UInt32 queueSize = 0;
|
|
|
MonitoredItem_queuedValue *queueItem;
|
|
|
|
|
@@ -399,65 +395,44 @@ UA_Boolean MonitoredItem_CopyMonitoredValueToVariant(UA_UInt32 attributeID, cons
|
|
|
}
|
|
|
|
|
|
void MonitoredItem_QueuePushDataValue(UA_Server *server, UA_MonitoredItem *monitoredItem) {
|
|
|
- UA_ByteString newValueAsByteString = { .length=0, .data=NULL };
|
|
|
- size_t encodingOffset = 0;
|
|
|
|
|
|
- if(!monitoredItem || monitoredItem->lastSampled + monitoredItem->samplingInterval > UA_DateTime_now())
|
|
|
- return;
|
|
|
+ /* if(monitoredItem->lastSampled + (UA_MSEC_TO_DATETIME * monitoredItem->samplingInterval) > UA_DateTime_now()) */
|
|
|
+ /* return; */
|
|
|
|
|
|
// FIXME: Actively suppress non change value based monitoring. There should be
|
|
|
// another function to handle status and events.
|
|
|
if(monitoredItem->monitoredItemType != MONITOREDITEM_TYPE_CHANGENOTIFY)
|
|
|
return;
|
|
|
|
|
|
+ /* Verify that the node being monitored is still valid */
|
|
|
+ const UA_Node *target = UA_NodeStore_get(server->nodestore, &monitoredItem->monitoredNodeId);
|
|
|
+ if(!target)
|
|
|
+ return;
|
|
|
+
|
|
|
MonitoredItem_queuedValue *newvalue = UA_malloc(sizeof(MonitoredItem_queuedValue));
|
|
|
if(!newvalue)
|
|
|
return;
|
|
|
-
|
|
|
- newvalue->listEntry.tqe_next = NULL;
|
|
|
- newvalue->listEntry.tqe_prev = NULL;
|
|
|
UA_DataValue_init(&newvalue->value);
|
|
|
-
|
|
|
- // Verify that the *Node being monitored is still valid
|
|
|
- // Looking up the in the nodestore is only necessary if we suspect that it is changed during writes
|
|
|
- // e.g. in multithreaded applications
|
|
|
- const UA_Node *target = UA_NodeStore_get(server->nodestore, &monitoredItem->monitoredNodeId);
|
|
|
- if(!target) {
|
|
|
- UA_free(newvalue);
|
|
|
- return;
|
|
|
- }
|
|
|
|
|
|
UA_Boolean samplingError = MonitoredItem_CopyMonitoredValueToVariant(monitoredItem->attributeID, target,
|
|
|
&newvalue->value);
|
|
|
|
|
|
- if(samplingError != false || !newvalue->value.value.type) {
|
|
|
+ if(samplingError || !newvalue->value.value.type) {
|
|
|
UA_DataValue_deleteMembers(&newvalue->value);
|
|
|
UA_free(newvalue);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- if(monitoredItem->queueSize.current >= monitoredItem->queueSize.max) {
|
|
|
- if(monitoredItem->discardOldest != true) {
|
|
|
- // We cannot remove the oldest value and theres no queue space left. We're done here.
|
|
|
- UA_DataValue_deleteMembers(&newvalue->value);
|
|
|
- UA_free(newvalue);
|
|
|
- return;
|
|
|
- }
|
|
|
- MonitoredItem_queuedValue *queueItem = TAILQ_LAST(&monitoredItem->queue, QueueOfQueueDataValues);
|
|
|
- TAILQ_REMOVE(&monitoredItem->queue, queueItem, listEntry);
|
|
|
- UA_free(queueItem);
|
|
|
- monitoredItem->queueSize.current--;
|
|
|
- }
|
|
|
-
|
|
|
- // encode the data to find if its different to the previous
|
|
|
+
|
|
|
+ /* encode the data */
|
|
|
size_t binsize = UA_calcSizeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_DATAVALUE]);
|
|
|
+ UA_ByteString newValueAsByteString;
|
|
|
UA_StatusCode retval = UA_ByteString_allocBuffer(&newValueAsByteString, binsize);
|
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
|
UA_DataValue_deleteMembers(&newvalue->value);
|
|
|
UA_free(newvalue);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+ size_t encodingOffset = 0;
|
|
|
retval = UA_encodeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_DATAVALUE], &newValueAsByteString, &encodingOffset);
|
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
|
UA_ByteString_deleteMembers(&newValueAsByteString);
|
|
@@ -465,24 +440,54 @@ void MonitoredItem_QueuePushDataValue(UA_Server *server, UA_MonitoredItem *monit
|
|
|
UA_free(newvalue);
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ /* did the content change? */
|
|
|
+ if(monitoredItem->lastSampledValue.data &&
|
|
|
+ UA_String_equal(&newValueAsByteString, &monitoredItem->lastSampledValue)) {
|
|
|
+ UA_DataValue_deleteMembers(&newvalue->value);
|
|
|
+ UA_free(newvalue);
|
|
|
+ UA_String_deleteMembers(&newValueAsByteString);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if(!monitoredItem->lastSampledValue.data) {
|
|
|
- UA_ByteString_copy(&newValueAsByteString, &monitoredItem->lastSampledValue);
|
|
|
- TAILQ_INSERT_HEAD(&monitoredItem->queue, newvalue, listEntry);
|
|
|
- monitoredItem->queueSize.current++;
|
|
|
- monitoredItem->lastSampled = UA_DateTime_now();
|
|
|
- UA_free(newValueAsByteString.data);
|
|
|
- } else {
|
|
|
- if(UA_String_equal(&newValueAsByteString, &monitoredItem->lastSampledValue) == true) {
|
|
|
+ /* do we have space? */
|
|
|
+ if(monitoredItem->queueSize.current >= monitoredItem->queueSize.max) {
|
|
|
+ if(!monitoredItem->discardOldest) {
|
|
|
+ // We cannot remove the oldest value and theres no queue space left. We're done here.
|
|
|
UA_DataValue_deleteMembers(&newvalue->value);
|
|
|
UA_free(newvalue);
|
|
|
UA_String_deleteMembers(&newValueAsByteString);
|
|
|
return;
|
|
|
}
|
|
|
- UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
|
|
|
- monitoredItem->lastSampledValue = newValueAsByteString;
|
|
|
- TAILQ_INSERT_HEAD(&monitoredItem->queue, newvalue, listEntry);
|
|
|
- monitoredItem->queueSize.current++;
|
|
|
- monitoredItem->lastSampled = UA_DateTime_now();
|
|
|
+ MonitoredItem_queuedValue *queueItem = TAILQ_LAST(&monitoredItem->queue, QueueOfQueueDataValues);
|
|
|
+ TAILQ_REMOVE(&monitoredItem->queue, queueItem, listEntry);
|
|
|
+ UA_free(queueItem);
|
|
|
+ monitoredItem->queueSize.current--;
|
|
|
}
|
|
|
+
|
|
|
+ /* add the sample */
|
|
|
+ UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
|
|
|
+ monitoredItem->lastSampledValue = newValueAsByteString;
|
|
|
+ TAILQ_INSERT_HEAD(&monitoredItem->queue, newvalue, listEntry);
|
|
|
+ monitoredItem->queueSize.current++;
|
|
|
+}
|
|
|
+
|
|
|
+UA_StatusCode MonitoredItem_registerSampleJob(UA_Server *server, UA_MonitoredItem *mon) {
|
|
|
+ if(mon->samplingInterval <= 5 )
|
|
|
+ return UA_STATUSCODE_BADNOTSUPPORTED;
|
|
|
+
|
|
|
+ UA_Job job = (UA_Job) {.type = UA_JOBTYPE_METHODCALL,
|
|
|
+ .job.methodCall = {.method = (UA_ServerCallback)MonitoredItem_QueuePushDataValue,
|
|
|
+ .data = mon} };
|
|
|
+
|
|
|
+ UA_StatusCode retval = UA_Server_addRepeatedJob(server, job, (UA_UInt32)mon->samplingInterval,
|
|
|
+ &mon->sampleJobGuid);
|
|
|
+ if(retval == UA_STATUSCODE_GOOD)
|
|
|
+ mon->sampleJobIsRegistered = true;
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+UA_StatusCode MonitoredItem_unregisterUpdateJob(UA_Server *server, UA_MonitoredItem *mon) {
|
|
|
+ mon->sampleJobIsRegistered = false;
|
|
|
+ return UA_Server_removeRepeatedJob(server, mon->sampleJobGuid);
|
|
|
}
|