Browse Source

Server: Two new callbacks to UA_GlobalNodeLifecycle

1. generateChildNodeId: Used to define NodeIds for automatically generated nodes during recursive node instantiation
2. createOptionalChild: Used to define whether an optional child should be created during recursive node instantiation
inspektor-scherlein 4 years ago
parent
commit
13fa838d54

+ 44 - 1
include/open62541/server.h

@@ -670,7 +670,50 @@ typedef struct {
     void (*destructor)(UA_Server *server,
                        const UA_NodeId *sessionId, void *sessionContext,
                        const UA_NodeId *nodeId, void *nodeContext);
-} UA_GlobalNodeLifecycle;
+
+    /* Can be NULL. Called during recursive node instantiation. While mandatory
+     * child nodes are automatically created if not already present, optional child
+     * nodes are not. This callback can be used to define whether an optional child
+     * node should be created.
+     *
+     * @param server The server executing the callback
+     * @param sessionId The identifier of the session
+     * @param sessionContext Additional data attached to the session in the
+     *        access control layer
+     * @param sourceNodeId Source node from the type definition. If the new node
+     *        shall be created, it will be a copy of this node.
+     * @param targetParentNodeId Parent of the potential new child node
+     * @param referenceTypeId Identifies the reference type which that the parent
+     *        node has to the new node. 
+     * @return Return UA_TRUE if the child node shall be instantiatet, 
+     *         UA_FALSE otherwise. */
+    UA_Boolean (*createOptionalChild)(UA_Server *server,
+                                      const UA_NodeId *sessionId,
+                                      void *sessionContext,
+                                      const UA_NodeId *sourceNodeId,
+                                      const UA_NodeId *targetParentNodeId,
+                                      const UA_NodeId *referenceTypeId);
+
+    /* Can be NULL. Called when a node is to be copied during recursive
+     * node instantiation. Allows definition of the NodeId for the new node.
+     * If the callback is set to NULL or the resulting NodeId is UA_NODEID_NULL,
+     * then a random NodeId will be generated.
+     *
+     * @param server The server executing the callback
+     * @param sessionId The identifier of the session
+     * @param sessionContext Additional data attached to the session in the
+     *        access control layer
+     * @param sourceNodeId Source node of the copy operation
+     * @param targetParentNodeId Parent node of the new node
+     * @param referenceTypeId Identifies the reference type which that the parent
+     *        node has to the new node. */
+    UA_StatusCode (*generateChildNodeId)(UA_Server *server,
+                                         const UA_NodeId *sessionId, void *sessionContext,
+                                         const UA_NodeId *sourceNodeId,
+                                         const UA_NodeId *targetParentNodeId,
+                                         const UA_NodeId *referenceTypeId,
+                                         UA_NodeId *targetNodeId);
+    } UA_GlobalNodeLifecycle;
 
 typedef struct {
     /* Can be NULL. May replace the nodeContext */

+ 2 - 0
plugins/ua_config_default.c

@@ -158,6 +158,8 @@ setDefaultConfig(UA_ServerConfig *conf) {
     /* Global Node Lifecycle */
     conf->nodeLifecycle.constructor = NULL;
     conf->nodeLifecycle.destructor = NULL;
+    conf->nodeLifecycle.createOptionalChild = NULL;
+    conf->nodeLifecycle.generateChildNodeId = NULL;
 
     /* Relax constraints for the InformationModel */
     conf->relaxEmptyValueConstraint = true; /* Allow empty values */

+ 29 - 3
src/server/ua_services_nodemanagement.c

@@ -443,9 +443,21 @@ copyChild(UA_Server *server, UA_Session *session, const UA_NodeId *destinationNo
         return retval;
     }
 
-    /* Is the child mandatory? If not, skip */
-    if(!isMandatoryChild(server, session, &rd->nodeId.nodeId))
-        return UA_STATUSCODE_GOOD;
+    /* Is the child mandatory? If not, ask callback whether child should be instantiated.
+     * If not, skip. */
+    if(!isMandatoryChild(server, session, &rd->nodeId.nodeId)) {
+        if(!server->config.nodeLifecycle.createOptionalChild)
+            return UA_STATUSCODE_GOOD;
+
+        if(server->config.nodeLifecycle.createOptionalChild(server,
+                                                            &session->sessionId,
+                                                            session->sessionHandle,
+                                                            &rd->nodeId.nodeId,
+                                                            destinationNodeId,
+                                                            &rd->referenceTypeId) == UA_FALSE) {
+            return UA_STATUSCODE_GOOD;
+        }
+    }
 
     /* Child is a method -> create a reference */
     if(rd->nodeClass == UA_NODECLASS_METHOD) {
@@ -477,6 +489,20 @@ copyChild(UA_Server *server, UA_Session *session, const UA_NodeId *destinationNo
         UA_NodeId_deleteMembers(&node->nodeId);
         node->nodeId.namespaceIndex = destinationNodeId->namespaceIndex;
 
+        if (server->config.nodeLifecycle.generateChildNodeId) {
+            retval = server->config.nodeLifecycle.generateChildNodeId(server,
+                                                                      &session->sessionId, session->sessionHandle,
+                                                                      &rd->nodeId.nodeId,
+                                                                      destinationNodeId,
+                                                                      &rd->referenceTypeId,
+                                                                      &node->nodeId);
+            if(retval != UA_STATUSCODE_GOOD) {
+                UA_Nodestore_deleteNode(server->nsCtx, node);
+                return retval;
+            }
+        }
+
+
         /* Remove references, they are re-created from scratch in addnode_finish */
         /* TODO: Be more clever in removing references that are re-added during
          * addnode_finish. That way, we can call addnode_finish also on children that were

+ 2 - 0
tests/server/check_services_nodemanagement.c

@@ -31,6 +31,8 @@ static void setup(void) {
     UA_GlobalNodeLifecycle lifecycle;
     lifecycle.constructor = globalInstantiationMethod;
     lifecycle.destructor = NULL;
+    lifecycle.createOptionalChild = NULL;
+    lifecycle.generateChildNodeId = NULL;
     config->nodeLifecycle = lifecycle;
 }