Browse Source

instantiate only children marked as mandatory

Julius Pfrommer 7 years ago
parent
commit
ee53610747
3 changed files with 88 additions and 23 deletions
  1. 22 9
      examples/tutorial_server_object.c
  2. 18 0
      src/server/ua_server.c
  3. 48 14
      src/server/ua_services_nodemanagement.c

+ 22 - 9
examples/tutorial_server_object.c

@@ -133,7 +133,7 @@ manuallyDefinePump(UA_Server *server) {
  *
  *    { rank=same
  *      point_1 [shape=point]
- *      node_1 [label=< <I>VariableNode</I><BR/>ManufacturerName >] }
+ *      node_1 [label=< <I>VariableNode</I><BR/>ManufacturerName<BR/>(mandatory) >] }
  *    node_root -> point_1 [arrowhead=none]
  *    point_1 -> node_1 [label="hasComponent"]
  *
@@ -151,7 +151,7 @@ manuallyDefinePump(UA_Server *server) {
  *
  *    {  rank=same
  *       point_4 [shape=point]
- *       node_4 [label=< <I>VariableNode</I><BR/>Status >] }
+ *       node_4 [label=< <I>VariableNode</I><BR/>Status<BR/>(mandatory) >] }
  *    node_3 -> point_4 [arrowhead=none]
  *    point_4 -> node_4 [label="hasComponent"]
  *
@@ -163,7 +163,9 @@ manuallyDefinePump(UA_Server *server) {
  *
  *    }
  *
- */
+ * Children that are marked mandatory are automatically instantiated together
+ * with the parent object. This is indicated by a `hasModellingRule` reference
+ * to an object that representes the `mandatory` modelling rule. */
 
 /* predefined identifier for later use */
 UA_NodeId pumpTypeId = {1, UA_NODEIDTYPE_NUMERIC, {1001}};
@@ -184,10 +186,16 @@ defineObjectTypes(UA_Server *server) {
     UA_VariableAttributes mnAttr;
     UA_VariableAttributes_init(&mnAttr);
     mnAttr.displayName = UA_LOCALIZEDTEXT("en_US", "ManufacturerName");
+    UA_NodeId manufacturerNameId;
     UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ManufacturerName"),
-                              UA_NODEID_NULL, mnAttr, NULL, NULL);
+                              UA_NODEID_NULL, mnAttr, NULL, &manufacturerNameId);
+    /* Make the manufacturer name mandatory */
+    UA_Server_addReference(server, manufacturerNameId,
+                           UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
+                           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
+
 
     UA_VariableAttributes modelAttr;
     UA_VariableAttributes_init(&modelAttr);
@@ -210,10 +218,15 @@ defineObjectTypes(UA_Server *server) {
     UA_VariableAttributes_init(&statusAttr);
     statusAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Status");
     statusAttr.valueRank = -1;
+    UA_NodeId statusId;
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "Status"),
-                              UA_NODEID_NULL, statusAttr, NULL, NULL);
+                              UA_NODEID_NULL, statusAttr, NULL, &statusId);
+    /* Make the status variable mandatory */
+    UA_Server_addReference(server, statusId,
+                           UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
+                           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
 
     UA_VariableAttributes rpmAttr;
     UA_VariableAttributes_init(&rpmAttr);
@@ -227,10 +240,10 @@ defineObjectTypes(UA_Server *server) {
 
 /**
  * Now we add the derived ObjectType for the pump that inherits from the device
- * object type. The resulting object contains all four inherited child
- * variables. The object has a reference of type ``hasTypeDefinition`` to the
- * object type. Clients can browse this information at runtime and adjust
- * accordingly.
+ * object type. The resulting object contains all mandatory child variables.
+ * These are simply copied over from the object type. The object has a reference
+ * of type ``hasTypeDefinition`` to the object type, so that clients can detect
+ * the type-instance relation at runtime.
  */
 
 static void

+ 18 - 0
src/server/ua_server.c

@@ -869,6 +869,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_NodeStore_insert(server->nodestore, (UA_Node*)baseobjtype);
     UA_RCU_UNLOCK();
 
+    addObjectTypeNode(server, "ModellingRuleType", UA_NS0ID_MODELLINGRULETYPE,
+                      UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "FolderType", UA_NS0ID_FOLDERTYPE,
                       UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "ServerType", UA_NS0ID_SERVERTYPE,
@@ -954,6 +956,22 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternalWithType(server, (UA_Node*)views, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
                             nodeIdOrganizes, nodeIdFolderType);
 
+    /*******************/
+    /* Modelling Rules */
+    /*******************/
+
+    UA_ObjectNode *mandatory = UA_NodeStore_newObjectNode();
+    copyNames((UA_Node*)mandatory, "Mandatory");
+    mandatory->nodeId.identifier.numeric = UA_NS0ID_MODELLINGRULE_MANDATORY;
+    addNodeInternalWithType(server, (UA_Node*)mandatory, UA_NODEID_NULL,
+                            UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULETYPE));
+
+    UA_ObjectNode *optional = UA_NodeStore_newObjectNode();
+    copyNames((UA_Node*)optional, "Optional");
+    optional->nodeId.identifier.numeric = UA_NS0ID_MODELLINGRULE_OPTIONAL;
+    addNodeInternalWithType(server, (UA_Node*)optional, UA_NODEID_NULL,
+                            UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULETYPE));
+
 #else
     /* load the generated namespace externally */
     ua_namespaceinit_generated(server);

+ 48 - 14
src/server/ua_services_nodemanagement.c

@@ -15,6 +15,11 @@
 static UA_StatusCode
 checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
                      const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
+    /* Objects do not need a parent (e.g. mandatory/optional modellingrules) */
+    if(nodeClass == UA_NODECLASS_OBJECT && UA_NodeId_isNull(parentNodeId) &&
+       UA_NodeId_isNull(referenceTypeId))
+        return UA_STATUSCODE_GOOD;
+
     /* See if the parent exists */
     const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
     if(!parent) {
@@ -327,6 +332,24 @@ instanceFindAggregateByBrowsename(UA_Server *server, UA_Session *session,
     return retval;
 }
 
+static UA_Boolean
+mandatoryChild(UA_Server *server, UA_Session *session, const UA_NodeId *childNodeId) {
+    const UA_Node *child = UA_NodeStore_get(server->nodestore, childNodeId);
+    const UA_NodeId mandatoryId = UA_NODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY);
+    const UA_NodeId hasModellingRuleId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE);
+    for(size_t i = 0; i < child->referencesSize; ++i) {
+        UA_ReferenceNode *ref = &child->references[i];
+        if(!UA_NodeId_equal(&hasModellingRuleId, &ref->referenceTypeId))
+            continue;
+        if(!UA_NodeId_equal(&mandatoryId, &ref->targetId.nodeId))
+            continue;
+        if(ref->isInverse)
+            continue;
+        return true;
+    }
+    return false;
+}
+
 /* Copy any children of Node sourceNodeId to another node destinationNodeId
  * Used at 2 places:
  *  (1) During instantiation, when any children of the Type are copied
@@ -360,7 +383,15 @@ copyChildNodesToNode(UA_Server* server, UA_Session* session,
     UA_NodeId existingChild = UA_NODEID_NULL;
     for(size_t i = 0; i < br.referencesSize; ++i) {
         UA_ReferenceDescription *rd = &br.references[i];
-        // Check for deduplication
+
+        /* Is the child mandatory? If not, skip */
+        if(!mandatoryChild(server, session, &rd->nodeId.nodeId))
+            continue;
+
+        /* TODO: If a child is optional, check whether optional children that
+         * were manually added fit the constraints. */
+
+        /* Check for deduplication */
         retval = instanceFindAggregateByBrowsename(server, session, destinationNodeId,
                                                    &rd->browseName, &existingChild);
         if(retval != UA_STATUSCODE_GOOD)
@@ -430,7 +461,7 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
                                                 parentNodeId, referenceTypeId);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Checking the reference to the parent returned"
+                            "AddNodes: Checking the reference to the parent returned "
                             "error code %s", UA_StatusCode_name(retval));
         UA_NodeStore_deleteNode(node);
         return retval;
@@ -456,18 +487,20 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     }
 
     /* Hierarchical reference back to the parent */
-    UA_AddReferencesItem item;
-    UA_AddReferencesItem_init(&item);
-    item.sourceNodeId = node->nodeId;
-    item.referenceTypeId = *referenceTypeId;
-    item.isForward = false;
-    item.targetNodeId.nodeId = *parentNodeId;
-    retval = Service_AddReferences_single(server, session, &item);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Could not add the reference to the parent"
-                            "with error code %s", UA_StatusCode_name(retval));
-        goto remove_node;
+    if(!UA_NodeId_isNull(parentNodeId)) {
+        UA_AddReferencesItem item;
+        UA_AddReferencesItem_init(&item);
+        item.sourceNodeId = node->nodeId;
+        item.referenceTypeId = *referenceTypeId;
+        item.isForward = false;
+        item.targetNodeId.nodeId = *parentNodeId;
+        retval = Service_AddReferences_single(server, session, &item);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "AddNodes: Could not add the reference to the parent"
+                                "with error code %s", UA_StatusCode_name(retval));
+            goto remove_node;
+        }
     }
 
     /* Fall back to a default typedefinition for variables and objects */
@@ -1204,6 +1237,7 @@ Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
     /* TODO: check if the information model consistency is violated */
+    /* TODO: Check if the node is a mandatory child of an object */
 
     /* Destroy an object before removing it */
     if(node->nodeClass == UA_NODECLASS_OBJECT) {