|
@@ -335,6 +335,77 @@ static void
|
|
addReference(UA_Server *server, UA_Session *session,
|
|
addReference(UA_Server *server, UA_Session *session,
|
|
const UA_AddReferencesItem *item, UA_StatusCode *retval);
|
|
const UA_AddReferencesItem *item, UA_StatusCode *retval);
|
|
|
|
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * This method only deletes references from the node which are not matching any type in the given array.
|
|
|
|
+ * Could be used to e.g. delete all the references, except 'HASMODELINGRULE'
|
|
|
|
+ */
|
|
|
|
+static void deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_NodeId* referencesSkip) {
|
|
|
|
+ if (referencesSkipSize == 0) {
|
|
|
|
+ UA_Node_deleteReferences(node);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Let's count if there are references left. If not just delete all the references.
|
|
|
|
+ * It's faster */
|
|
|
|
+ size_t newSize = 0;
|
|
|
|
+ for(size_t i = 0; i < node->referencesSize; ++i) {
|
|
|
|
+ for (size_t j = 0; j < referencesSkipSize; j++) {
|
|
|
|
+ if (UA_NodeId_equal(&node->references[i].referenceTypeId, &referencesSkip[j])) {
|
|
|
|
+ newSize++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (newSize == 0){
|
|
|
|
+ UA_Node_deleteReferences(node);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Now copy the remaining references to a new array */
|
|
|
|
+ UA_NodeReferenceKind *newReferences = (UA_NodeReferenceKind *)UA_malloc(sizeof(UA_NodeReferenceKind) * (newSize));
|
|
|
|
+ size_t curr = 0;
|
|
|
|
+ UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
|
+ for(size_t i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; ++i) {
|
|
|
|
+ for (size_t j = 0; j < referencesSkipSize; j++) {
|
|
|
|
+ if (!UA_NodeId_equal(&node->references[i].referenceTypeId, &referencesSkip[j]))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ // copy the reference
|
|
|
|
+ UA_NodeReferenceKind *srefs = &node->references[i];
|
|
|
|
+ UA_NodeReferenceKind *drefs = &newReferences[curr++];
|
|
|
|
+ drefs->isInverse = srefs->isInverse;
|
|
|
|
+ retval = UA_NodeId_copy(&srefs->referenceTypeId, &drefs->referenceTypeId);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
|
+ break;
|
|
|
|
+ retval = UA_Array_copy(srefs->targetIds, srefs->targetIdsSize,
|
|
|
|
+ (void**)&drefs->targetIds,
|
|
|
|
+ &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
|
+ break;
|
|
|
|
+ drefs->targetIdsSize = srefs->targetIdsSize;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ if (retval != UA_STATUSCODE_GOOD) {
|
|
|
|
+ for (size_t k=0; k<i; k++) {
|
|
|
|
+ UA_NodeReferenceKind *refs = &newReferences[i];
|
|
|
|
+ for(size_t j = 0; j < refs->targetIdsSize; ++j)
|
|
|
|
+ UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j]);
|
|
|
|
+ UA_Array_delete(refs->targetIds, refs->targetIdsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
|
|
|
|
+ UA_NodeId_deleteMembers(&refs->referenceTypeId);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ UA_Node_deleteReferences(node);
|
|
|
|
+ if (retval == UA_STATUSCODE_GOOD) {
|
|
|
|
+ node->references = newReferences;
|
|
|
|
+ node->referencesSize = newSize;
|
|
|
|
+ } else {
|
|
|
|
+ UA_free(newReferences);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
static UA_StatusCode
|
|
static UA_StatusCode
|
|
copyChildNode(UA_Server *server, UA_Session *session,
|
|
copyChildNode(UA_Server *server, UA_Session *session,
|
|
const UA_NodeId *destinationNodeId,
|
|
const UA_NodeId *destinationNodeId,
|
|
@@ -399,7 +470,9 @@ copyChildNode(UA_Server *server, UA_Session *session,
|
|
/* TODO: Be more clever in removing references that are re-added during
|
|
/* 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
|
|
* addnode_finish. That way, we can call addnode_finish also on children that were
|
|
* manually added by the user during addnode_begin and addnode_finish. */
|
|
* manually added by the user during addnode_begin and addnode_finish. */
|
|
- UA_Node_deleteReferences(node);
|
|
|
|
|
|
+ /* For now we keep all the modelling rule references and delete all others */
|
|
|
|
+ UA_NodeId modellingRuleReferenceId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE);
|
|
|
|
+ deleteReferencesSubset(node, 1, &modellingRuleReferenceId);
|
|
|
|
|
|
/* Add the node to the nodestore */
|
|
/* Add the node to the nodestore */
|
|
UA_NodeId newNodeId;
|
|
UA_NodeId newNodeId;
|
|
@@ -409,8 +482,14 @@ copyChildNode(UA_Server *server, UA_Session *session,
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
- /* Call addnode_finish, this recursively adds members, the type
|
|
|
|
- * definition and so on */
|
|
|
|
|
|
+ /* Add all the children of this child to the new child node to make sure we take
|
|
|
|
+ * the values from the nearest inherited object first.
|
|
|
|
+ */
|
|
|
|
+ copyChildNodes(server, session, &rd->nodeId.nodeId, &newNodeId);
|
|
|
|
+
|
|
|
|
+ /* Call addnode_finish, this recursively adds additional members, the type
|
|
|
|
+ * definition and so on of the base type of this child, if they are not yet
|
|
|
|
+ * in the destination */
|
|
retval = Operation_addNode_finish(server, session, &newNodeId, destinationNodeId,
|
|
retval = Operation_addNode_finish(server, session, &newNodeId, destinationNodeId,
|
|
&rd->referenceTypeId, typeId);
|
|
&rd->referenceTypeId, typeId);
|
|
UA_NodeId_deleteMembers(&newNodeId);
|
|
UA_NodeId_deleteMembers(&newNodeId);
|