Bladeren bron

Subscriptions: Sample values without copying where possible

Julius Pfrommer 5 jaren geleden
bovenliggende
commit
f7b8caad08
3 gewijzigde bestanden met toevoegingen van 48 en 19 verwijderingen
  1. 8 0
      src/server/ua_server_internal.h
  2. 6 6
      src/server/ua_services_attribute.c
  3. 34 13
      src/server/ua_subscription_datachange.c

+ 8 - 0
src/server/ua_server_internal.h

@@ -327,6 +327,14 @@ UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
 /* Check Information Model Consistency */
 /***************************************/
 
+/* Read a node attribute in the context of a "checked-out" node. So the
+ * attribute will not be copied when possible. The variant then points into the
+ * node and has UA_VARIANT_DATA_NODELETE set. */
+void
+ReadWithNode(const UA_Node *node, UA_Server *server, UA_Session *session,
+             UA_TimestampsToReturn timestampsToReturn,
+             const UA_ReadValueId *id, UA_DataValue *v);
+
 UA_StatusCode
 readValueAttribute(UA_Server *server, UA_Session *session,
                    const UA_VariableNode *vn, UA_DataValue *v);

+ 6 - 6
src/server/ua_services_attribute.c

@@ -190,10 +190,10 @@ static const UA_String jsonEncoding = {sizeof("Default JSON")-1, (UA_Byte*)"Defa
 /* Returns a datavalue that may point into the node via the
  * UA_VARIANT_DATA_NODELETE tag. Don't access the returned DataValue once the
  * node has been released! */
-static void
-Read(const UA_Node *node, UA_Server *server, UA_Session *session,
-     UA_TimestampsToReturn timestampsToReturn,
-     const UA_ReadValueId *id, UA_DataValue *v) {
+void
+ReadWithNode(const UA_Node *node, UA_Server *server, UA_Session *session,
+             UA_TimestampsToReturn timestampsToReturn,
+             const UA_ReadValueId *id, UA_DataValue *v) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Read the attribute %i", id->attributeId);
 
@@ -376,7 +376,7 @@ Operation_Read(UA_Server *server, UA_Session *session, UA_MessageContext *mc,
 
     /* Perform the read operation */
     if(node) {
-        Read(node, server, session, timestampsToReturn, id, &dv);
+        ReadWithNode(node, server, session, timestampsToReturn, id, &dv);
     } else {
         dv.hasStatus = true;
         dv.status = UA_STATUSCODE_BADNODEIDUNKNOWN;
@@ -456,7 +456,7 @@ UA_Server_readWithSession(UA_Server *server, UA_Session *session,
     }
 
     /* Perform the read operation */
-    Read(node, server, session, timestampsToReturn, item, &dv);
+    ReadWithNode(node, server, session, timestampsToReturn, item, &dv);
 
     /* Do we have to copy the result before releasing the node? */
     if(dv.hasValue && dv.value.storageType == UA_VARIANT_DATA_NODELETE) {

+ 34 - 13
src/server/ua_subscription_datachange.c

@@ -230,13 +230,22 @@ sampleCallbackWithValue(UA_Server *server, UA_MonitoredItem *monitoredItem,
             return false;
         }
 
-        /* <-- Point of no return --> */
+        if(value->value.storageType == UA_VARIANT_DATA) {
+            newNotification->data.value = *value; /* Move the value to the notification */
+            storedValue = true;
+        } else { /* => (value->value.storageType == UA_VARIANT_DATA_NODELETE) */
+            UA_StatusCode retval = UA_DataValue_copy(value, &newNotification->data.value);
+            if(retval != UA_STATUSCODE_GOOD) {
+                UA_ByteString_deleteMembers(&binaryEncoding);
+                UA_free(newNotification);
+                return false;
+            }
+        }
 
-        newNotification->mon = monitoredItem;
-        newNotification->data.value = *value; /* Move the value to the notification */
-        storedValue = true;
+        /* <-- Point of no return --> */
 
         /* Enqueue the new notification */
+        newNotification->mon = monitoredItem;
         UA_Notification_enqueue(server, sub, monitoredItem, newNotification);
     } else {
         /* Call the local callback if not attached to a subscription */
@@ -295,21 +304,33 @@ UA_MonitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredIt
         return;
     }
 
-    /* Sample the value */
-    UA_ReadValueId rvid;
-    UA_ReadValueId_init(&rvid);
-    rvid.nodeId = monitoredItem->monitoredNodeId;
-    rvid.attributeId = monitoredItem->attributeId;
-    rvid.indexRange = monitoredItem->indexRange;
-    UA_DataValue value = UA_Server_readWithSession(server, session, &rvid,
-                                                   monitoredItem->timestampsToReturn);
+    /* Get the node */
+    const UA_Node *node = UA_Nodestore_get(server, &monitoredItem->monitoredNodeId);
+
+    /* Sample the value. The sample can still point into the node. */
+    UA_DataValue value;
+    UA_DataValue_init(&value);
+    if(node) {
+        UA_ReadValueId rvid;
+        UA_ReadValueId_init(&rvid);
+        rvid.nodeId = monitoredItem->monitoredNodeId;
+        rvid.attributeId = monitoredItem->attributeId;
+        rvid.indexRange = monitoredItem->indexRange;
+
+        ReadWithNode(node, server, session, monitoredItem->timestampsToReturn, &rvid, &value);
+    } else {
+        value.hasStatus = true;
+        value.status = UA_STATUSCODE_BADNODEIDUNKNOWN;
+    }
 
     /* Operate on the sample */
     UA_Boolean storedValue = sampleCallbackWithValue(server, monitoredItem, &value);
 
     /* Delete the sample if it was not stored in the MonitoredItem  */
     if(!storedValue)
-        UA_DataValue_deleteMembers(&value);
+        UA_DataValue_deleteMembers(&value); /* Does nothing for UA_VARIANT_DATA_NODELETE */
+    if(node)
+        UA_Nodestore_release(server, node);
 }
 
 #endif /* UA_ENABLE_SUBSCRIPTIONS */