|
@@ -71,8 +71,7 @@ copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *va
|
|
|
Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
|
|
|
|
|
|
/* Copy any aggregated/nested variables/methods/subobjects this object contains
|
|
|
- * These objects may not be part of the nodes type.
|
|
|
- */
|
|
|
+ * These objects may not be part of the nodes type. */
|
|
|
copyChildNodesToNode(server, session, &node->nodeId, &res.addedNodeId, instantiationCallback);
|
|
|
if(instantiationCallback)
|
|
|
instantiationCallback->method(res.addedNodeId, node->nodeId,
|
|
@@ -125,8 +124,7 @@ copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *obje
|
|
|
Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
|
|
|
|
|
|
/* Copy any aggregated/nested variables/methods/subobjects this object contains
|
|
|
- * These objects may not be part of the nodes type.
|
|
|
- */
|
|
|
+ * These objects may not be part of the nodes type. */
|
|
|
copyChildNodesToNode(server, session, &node->nodeId, &res.addedNodeId, instantiationCallback);
|
|
|
if(instantiationCallback)
|
|
|
instantiationCallback->method(res.addedNodeId, node->nodeId,
|
|
@@ -148,138 +146,62 @@ setObjectInstanceHandle(UA_Server *server, UA_Session *session,
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-instantiateObjectNode(UA_Server *server, UA_Session *session,
|
|
|
- const UA_NodeId *nodeId, const UA_NodeId *typeId, size_t depth,
|
|
|
- UA_InstantiationCallback *instantiationCallback) {
|
|
|
- /* see if the type is derived from baseobjecttype */
|
|
|
- UA_Boolean found = false;
|
|
|
- const UA_NodeId hassubtype = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
|
|
|
- const UA_NodeId baseobjtype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
|
|
|
- UA_StatusCode retval = isNodeInTree(server->nodestore, typeId,
|
|
|
- &baseobjtype, &hassubtype, 1, &found);
|
|
|
- if(!found || retval != UA_STATUSCODE_GOOD) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: The object if not derived from BaseObjectType");
|
|
|
- return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
|
|
|
- }
|
|
|
-
|
|
|
+instantiateNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
|
|
|
+ UA_NodeClass nodeClass, const UA_NodeId *typeId,
|
|
|
+ UA_InstantiationCallback *instantiationCallback) {
|
|
|
/* see if the type node is correct */
|
|
|
- const UA_ObjectTypeNode *typenode =
|
|
|
- (const UA_ObjectTypeNode*)UA_NodeStore_get(server->nodestore, typeId);
|
|
|
- if(!typenode || typenode->nodeClass != UA_NODECLASS_OBJECTTYPE || typenode->isAbstract)
|
|
|
+ const UA_Node *typenode = UA_NodeStore_get(server->nodestore, typeId);
|
|
|
+ if(!typenode)
|
|
|
return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
|
|
|
-
|
|
|
- /* Add all the child nodes */
|
|
|
- copyChildNodesToNode(server, session, typeId, nodeId, instantiationCallback);
|
|
|
-
|
|
|
- /* Instantiate supertype attributes if a supertype is available */
|
|
|
- UA_BrowseDescription browseChildren;
|
|
|
- UA_BrowseDescription_init(&browseChildren);
|
|
|
- browseChildren.nodeId = *typeId;
|
|
|
- browseChildren.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
|
|
|
- browseChildren.includeSubtypes = false;
|
|
|
- browseChildren.browseDirection = UA_BROWSEDIRECTION_INVERSE; // isSubtypeOf
|
|
|
- browseChildren.nodeClassMask = UA_NODECLASS_OBJECTTYPE;
|
|
|
- browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS;
|
|
|
-
|
|
|
- UA_BrowseResult browseResult;
|
|
|
- UA_BrowseResult_init(&browseResult);
|
|
|
- // todo: continuation points if there are too many results
|
|
|
- Service_Browse_single(server, session, NULL, &browseChildren, 100, &browseResult);
|
|
|
- for(size_t i = 0; i < browseResult.referencesSize; i++) {
|
|
|
- UA_ReferenceDescription *rd = &browseResult.references[i];
|
|
|
- instantiateObjectNode(server, session, nodeId, &rd->nodeId.nodeId,
|
|
|
- depth+1, instantiationCallback);
|
|
|
- }
|
|
|
- UA_BrowseResult_deleteMembers(&browseResult);
|
|
|
-
|
|
|
- /* add a hastypedefinition reference */
|
|
|
- if(depth == 0) {
|
|
|
- UA_AddReferencesItem addref;
|
|
|
- UA_AddReferencesItem_init(&addref);
|
|
|
- addref.sourceNodeId = *nodeId;
|
|
|
- addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
|
|
|
- addref.isForward = true;
|
|
|
- addref.targetNodeId.nodeId = *typeId;
|
|
|
- addref.targetNodeClass = UA_NODECLASS_OBJECTTYPE;
|
|
|
- Service_AddReferences_single(server, session, &addref);
|
|
|
- }
|
|
|
-
|
|
|
- /* call the constructor */
|
|
|
- const UA_ObjectLifecycleManagement *olm = &typenode->lifecycleManagement;
|
|
|
- if(olm->constructor)
|
|
|
- UA_Server_editNode(server, session, nodeId,
|
|
|
- (UA_EditNodeCallback)setObjectInstanceHandle,
|
|
|
- olm->constructor);
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
-}
|
|
|
-
|
|
|
-static UA_StatusCode
|
|
|
-instantiateVariableNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
|
|
|
- const UA_NodeId *typeId, size_t depth,
|
|
|
- UA_InstantiationCallback *instantiationCallback) {
|
|
|
- /* see if the type is derived from basevariabletype */
|
|
|
- UA_Boolean found = false;
|
|
|
- const UA_NodeId hassubtype = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
|
|
|
- const UA_NodeId basevartype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE);
|
|
|
- UA_StatusCode retval = isNodeInTree(server->nodestore, typeId,
|
|
|
- &basevartype, &hassubtype, 1, &found);
|
|
|
- if(!found || retval != UA_STATUSCODE_GOOD) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: The variable is not derived from BaseVariableType");
|
|
|
+ if(nodeClass == UA_NODECLASS_VARIABLE) {
|
|
|
+ if(typenode->nodeClass != UA_NODECLASS_VARIABLETYPE ||
|
|
|
+ ((const UA_VariableTypeNode*)typenode)->isAbstract)
|
|
|
+ return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
|
|
|
+ } else if(nodeClass == UA_NODECLASS_OBJECT) {
|
|
|
+ if(typenode->nodeClass != UA_NODECLASS_OBJECTTYPE ||
|
|
|
+ ((const UA_ObjectTypeNode*)typenode)->isAbstract)
|
|
|
+ return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
|
|
|
+ } else {
|
|
|
return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
|
|
|
}
|
|
|
|
|
|
- /* see if the type is correct */
|
|
|
- const UA_VariableTypeNode *typenode =
|
|
|
- (const UA_VariableTypeNode*)UA_NodeStore_get(server->nodestore, typeId);
|
|
|
- if(!typenode || typenode->nodeClass != UA_NODECLASS_VARIABLETYPE || typenode->isAbstract)
|
|
|
- return UA_STATUSCODE_BADNODEIDINVALID;
|
|
|
-
|
|
|
- /* get the references to child properties */
|
|
|
- /* Add all the child nodes */
|
|
|
- copyChildNodesToNode(server, session, typeId, nodeId, instantiationCallback);
|
|
|
+ /* Get the hierarchy of the type and all its supertypes */
|
|
|
+ UA_NodeId *hierarchy = NULL;
|
|
|
+ size_t hierarchySize = 0;
|
|
|
+ UA_StatusCode retval =
|
|
|
+ getTypeHierarchy(server->nodestore, typenode, true, &hierarchy, &hierarchySize);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ return retval;
|
|
|
|
|
|
- /* Instantiate supertypes */
|
|
|
- UA_BrowseDescription browseChildren;
|
|
|
- UA_BrowseDescription_init(&browseChildren);
|
|
|
- browseChildren.nodeId = *typeId;
|
|
|
- browseChildren.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
|
|
|
- browseChildren.includeSubtypes = false;
|
|
|
- browseChildren.browseDirection = UA_BROWSEDIRECTION_INVERSE; // isSubtypeOf
|
|
|
- browseChildren.nodeClassMask = UA_NODECLASS_VARIABLETYPE;
|
|
|
- browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS;
|
|
|
-
|
|
|
- UA_BrowseResult browseResult;
|
|
|
- UA_BrowseResult_init(&browseResult);
|
|
|
- // todo: continuation points if there are too many results
|
|
|
- Service_Browse_single(server, session, NULL, &browseChildren, 100, &browseResult);
|
|
|
- for(size_t i = 0; i < browseResult.referencesSize; i++) {
|
|
|
- UA_ReferenceDescription *rd = &browseResult.references[i];
|
|
|
- instantiateVariableNode(server, session, nodeId, &rd->nodeId.nodeId,
|
|
|
- depth+1, instantiationCallback);
|
|
|
- }
|
|
|
- UA_BrowseResult_deleteMembers(&browseResult);
|
|
|
+ /* Copy members of the type and supertypes */
|
|
|
+ for(size_t i = 0; i < hierarchySize; i++)
|
|
|
+ retval |= copyChildNodesToNode(server, session, &hierarchy[i], nodeId, instantiationCallback);
|
|
|
+ UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ return retval;
|
|
|
|
|
|
- /* add a hastypedefinition reference */
|
|
|
- if(depth == 0) {
|
|
|
- UA_AddReferencesItem addref;
|
|
|
- UA_AddReferencesItem_init(&addref);
|
|
|
- addref.sourceNodeId = *nodeId;
|
|
|
- addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
|
|
|
- addref.isForward = true;
|
|
|
- addref.targetNodeId.nodeId = *typeId;
|
|
|
- addref.targetNodeClass = UA_NODECLASS_OBJECTTYPE;
|
|
|
- /* TODO: Check result */
|
|
|
- Service_AddReferences_single(server, session, &addref);
|
|
|
+ /* Call the object constructor */
|
|
|
+ if(typenode->nodeClass == UA_NODECLASS_OBJECTTYPE) {
|
|
|
+ const UA_ObjectLifecycleManagement *olm =
|
|
|
+ &((const UA_ObjectTypeNode*)typenode)->lifecycleManagement;
|
|
|
+ if(olm->constructor)
|
|
|
+ UA_Server_editNode(server, session, nodeId,
|
|
|
+ (UA_EditNodeCallback)setObjectInstanceHandle,
|
|
|
+ olm->constructor);
|
|
|
}
|
|
|
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
-}
|
|
|
+ /* Add a hasType reference */
|
|
|
+ UA_AddReferencesItem addref;
|
|
|
+ UA_AddReferencesItem_init(&addref);
|
|
|
+ addref.sourceNodeId = *nodeId;
|
|
|
+ addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
|
|
|
+ addref.isForward = true;
|
|
|
+ addref.targetNodeId.nodeId = *typeId;
|
|
|
+ return Service_AddReferences_single(server, session, &addref);
|
|
|
+}
|
|
|
|
|
|
/* Search for an instance of "browseName" in node searchInstance
|
|
|
- * Used during copyChildNodes to find overwritable/mergable nodes
|
|
|
- */
|
|
|
+ * Used during copyChildNodes to find overwritable/mergable nodes */
|
|
|
static UA_NodeId
|
|
|
instanceFindAggregateByBrowsename(UA_Server *server, UA_Session *session,
|
|
|
const UA_NodeId *searchInstance,
|
|
@@ -332,7 +254,8 @@ copyChildNodesToNode(UA_Server* server, UA_Session* session,
|
|
|
browseChildren.includeSubtypes = true;
|
|
|
browseChildren.browseDirection = UA_BROWSEDIRECTION_FORWARD;
|
|
|
browseChildren.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
|
|
|
- browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME;
|
|
|
+ browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS |
|
|
|
+ UA_BROWSERESULTMASK_BROWSENAME;
|
|
|
|
|
|
UA_BrowseResult browseResult;
|
|
|
UA_BrowseResult_init(&browseResult);
|
|
@@ -342,7 +265,8 @@ copyChildNodesToNode(UA_Server* server, UA_Session* session,
|
|
|
for(size_t i = 0; i < browseResult.referencesSize; i++) {
|
|
|
UA_ReferenceDescription *rd = &browseResult.references[i];
|
|
|
// Check for deduplication
|
|
|
- UA_NodeId existing = instanceFindAggregateByBrowsename(server, session, destinationNodeId, &rd->browseName);
|
|
|
+ UA_NodeId existing = instanceFindAggregateByBrowsename(server, session, destinationNodeId,
|
|
|
+ &rd->browseName);
|
|
|
if(UA_NodeId_equal(&UA_NODEID_NULL, &existing)) { /* New node in child */
|
|
|
if(rd->nodeClass == UA_NODECLASS_METHOD) {
|
|
|
/* add a reference to the method in the objecttype */
|
|
@@ -362,15 +286,17 @@ copyChildNodesToNode(UA_Server* server, UA_Session* session,
|
|
|
&rd->referenceTypeId, destinationNodeId, instantiationCallback);
|
|
|
} else { /* Preexistent node in child */
|
|
|
/* General strategy if we meet an already existing node:
|
|
|
- * - Preexistent variable contents always 'win' overwriting anything supertypes would instantiate
|
|
|
- * - Always copy contents of template *into* existant node (merge contents of e.g. Folders like ParameterSet)
|
|
|
- */
|
|
|
+ * - Preexistent variable contents always 'win' overwriting anything
|
|
|
+ * supertypes would instantiate
|
|
|
+ * - Always copy contents of template *into* existant node (merge
|
|
|
+ * contents of e.g. Folders like ParameterSet) */
|
|
|
if(rd->nodeClass == UA_NODECLASS_METHOD) {
|
|
|
/* Do nothing, existent method wins */
|
|
|
} else if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
|
|
|
rd->nodeClass == UA_NODECLASS_OBJECT) {
|
|
|
if(!UA_NodeId_equal(&rd->nodeId.nodeId, &existing))
|
|
|
- copyChildNodesToNode(server, session, &rd->nodeId.nodeId, &existing, instantiationCallback);
|
|
|
+ copyChildNodesToNode(server, session, &rd->nodeId.nodeId,
|
|
|
+ &existing, instantiationCallback);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -463,19 +389,6 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
|
|
|
return UA_STATUSCODE_BADNODEIDINVALID;
|
|
|
}
|
|
|
|
|
|
- /* Fall back to a default typedefinition for variables and objects */
|
|
|
- const UA_NodeId basedatavariabletype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
|
|
|
- const UA_NodeId baseobjecttype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
|
|
|
- if(node->nodeClass == UA_NODECLASS_VARIABLE ||
|
|
|
- node->nodeClass == UA_NODECLASS_OBJECT) {
|
|
|
- if(!typeDefinition || UA_NodeId_isNull(typeDefinition)) {
|
|
|
- if(node->nodeClass == UA_NODECLASS_VARIABLE)
|
|
|
- typeDefinition = &basedatavariabletype;
|
|
|
- else
|
|
|
- typeDefinition = &baseobjecttype;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/* Check the reference to the parent */
|
|
|
UA_StatusCode retval = checkParentReference(server, session, node->nodeClass,
|
|
|
parentNodeId, referenceTypeId);
|
|
@@ -517,17 +430,26 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
|
|
|
goto remove_node;
|
|
|
}
|
|
|
|
|
|
- /* Instantiate variables and objects */
|
|
|
- if(node->nodeClass == UA_NODECLASS_OBJECT)
|
|
|
- retval = instantiateObjectNode(server, session, &node->nodeId,
|
|
|
- typeDefinition, 0, instantiationCallback);
|
|
|
- else if(node->nodeClass == UA_NODECLASS_VARIABLE)
|
|
|
- retval = instantiateVariableNode(server, session, &node->nodeId,
|
|
|
- typeDefinition, 0, instantiationCallback);
|
|
|
- if(retval != UA_STATUSCODE_GOOD) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: Could not instantiate the node");
|
|
|
- goto remove_node;
|
|
|
+ if(node->nodeClass == UA_NODECLASS_VARIABLE ||
|
|
|
+ node->nodeClass == UA_NODECLASS_OBJECT) {
|
|
|
+ /* Fall back to a default typedefinition for variables and objects */
|
|
|
+ const UA_NodeId basedatavariabletype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
|
|
|
+ const UA_NodeId baseobjecttype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
|
|
|
+ if(!typeDefinition || UA_NodeId_isNull(typeDefinition)) {
|
|
|
+ if(node->nodeClass == UA_NODECLASS_VARIABLE)
|
|
|
+ typeDefinition = &basedatavariabletype;
|
|
|
+ else
|
|
|
+ typeDefinition = &baseobjecttype;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Instantiate variables and objects */
|
|
|
+ retval = instantiateNode(server, session, &node->nodeId, node->nodeClass,
|
|
|
+ typeDefinition, instantiationCallback);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
+ UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
+ "AddNodes: Could not instantiate the node");
|
|
|
+ goto remove_node;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* Custom callback */
|
|
@@ -587,10 +509,6 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
|
|
|
if(!UA_NodeId_isNull(&attr->dataType))
|
|
|
retval = UA_VariableNode_setDataType(server, node, vt, &attr->dataType);
|
|
|
else /* workaround common error where the datatype is left as NA_NODEID_NULL */
|
|
|
- /* Note that most NS0 VarTypeNodes are DataTypes, not VariableTypes! */
|
|
|
- if (vt->nodeClass == UA_NODECLASS_DATATYPE)
|
|
|
- UA_NodeId_copy(&vt->nodeId, &node->dataType);
|
|
|
- else
|
|
|
retval = UA_VariableNode_setDataType(server, node, vt, &vt->dataType);
|
|
|
|
|
|
node->valueRank = -2; /* allow all dimensions first */
|