UA_StatusCode UA_EXPORT UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_QualifiedName browseName, const UA_NodeId typeDefinition, const UA_VariableAttributes attr, const UA_DataSource dataSource, UA_NodeId *outNewNodeId) { UA_AddNodesResult result; UA_AddNodesResult_init(&result); UA_AddNodesItem item; UA_AddNodesItem_init(&item); result.statusCode = UA_QualifiedName_copy(&browseName, &item.browseName); item.nodeClass = UA_NODECLASS_VARIABLE; result.statusCode |= UA_NodeId_copy(&parentNodeId, &item.parentNodeId.nodeId); result.statusCode |= UA_NodeId_copy(&referenceTypeId, &item.referenceTypeId); result.statusCode |= UA_NodeId_copy(&requestedNewNodeId, &item.requestedNewNodeId.nodeId); result.statusCode |= UA_NodeId_copy(&typeDefinition, &item.typeDefinition.nodeId); UA_VariableAttributes attrCopy; result.statusCode |= UA_VariableAttributes_copy(&attr, &attrCopy); if(result.statusCode != UA_STATUSCODE_GOOD) { UA_AddNodesItem_deleteMembers(&item); UA_VariableAttributes_deleteMembers(&attrCopy); return result.statusCode; } UA_VariableNode *node = UA_VariableNode_new(); if(!node) { UA_AddNodesItem_deleteMembers(&item); UA_VariableAttributes_deleteMembers(&attrCopy); return UA_STATUSCODE_BADOUTOFMEMORY; } copyStandardAttributes((UA_Node*)node, &item, (UA_NodeAttributes*)&attrCopy); node->valueSource = UA_VALUESOURCE_DATASOURCE; node->value.dataSource = dataSource; node->accessLevel = attr.accessLevel; node->userAccessLevel = attr.userAccessLevel; node->historizing = attr.historizing; node->minimumSamplingInterval = attr.minimumSamplingInterval; node->valueRank = attr.valueRank; UA_Server_addExistingNode(server, &adminSession, (UA_Node*)node, &item.parentNodeId.nodeId, &item.referenceTypeId, &result); UA_AddNodesItem_deleteMembers(&item); UA_VariableAttributes_deleteMembers(&attrCopy); if(result.statusCode != UA_STATUSCODE_GOOD) UA_VariableNode_delete(node); if(outNewNodeId && result.statusCode == UA_STATUSCODE_GOOD) *outNewNodeId = result.addedNodeId; else UA_AddNodesResult_deleteMembers(&result); return result.statusCode; }
/* copy an existing variable under the given parent. then instantiate the variable for all hastypedefinitions of the original version. */ static UA_StatusCode copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *variable, const UA_NodeId *referenceType, const UA_NodeId *parent) { const UA_VariableNode *node = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, variable); if(!node) return UA_STATUSCODE_BADNODEIDINVALID; if(node->nodeClass != UA_NODECLASS_VARIABLE) return UA_STATUSCODE_BADNODECLASSINVALID; // copy the variable attributes UA_VariableAttributes attr; UA_VariableAttributes_init(&attr); UA_LocalizedText_copy(&node->displayName, &attr.displayName); UA_LocalizedText_copy(&node->description, &attr.description); attr.writeMask = node->writeMask; attr.userWriteMask = node->userWriteMask; // todo: handle data sources!!!! UA_Variant_copy(&node->value.variant.value, &attr.value); // datatype is taken from the value // valuerank is taken from the value // array dimensions are taken from the value attr.accessLevel = node->accessLevel; attr.userAccessLevel = node->userAccessLevel; attr.minimumSamplingInterval = node->minimumSamplingInterval; attr.historizing = node->historizing; UA_AddNodesItem item; UA_AddNodesItem_init(&item); UA_NodeId_copy(parent, &item.parentNodeId.nodeId); UA_NodeId_copy(referenceType, &item.referenceTypeId); UA_QualifiedName_copy(&node->browseName, &item.browseName); item.nodeClass = UA_NODECLASS_VARIABLE; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES]; item.nodeAttributes.content.decoded.data = &attr; // don't add a typedefinition here. // add the new variable UA_AddNodesResult res; UA_AddNodesResult_init(&res); Service_AddNodes_single(server, session, &item, &res); UA_VariableAttributes_deleteMembers(&attr); UA_AddNodesItem_deleteMembers(&item); // now instantiate the variable for all hastypedefinition references for(size_t i = 0; i < node->referencesSize; i++) { UA_ReferenceNode *rn = &node->references[i]; if(rn->isInverse) continue; const UA_NodeId hasTypeDef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); if(!UA_NodeId_equal(&rn->referenceTypeId, &hasTypeDef)) continue; instantiateVariableNode(server, session, &res.addedNodeId, &rn->targetId.nodeId); } UA_AddNodesResult_deleteMembers(&res); return UA_STATUSCODE_GOOD; }
/* copy an existing object under the given parent. then instantiate the variable for all hastypedefinitions of the original version. */ static UA_StatusCode copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *variable, const UA_NodeId *referenceType, const UA_NodeId *parent, UA_InstantiationCallback *instantiationCallback) { const UA_ObjectNode *node = (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, variable); if(!node) return UA_STATUSCODE_BADNODEIDINVALID; if(node->nodeClass != UA_NODECLASS_OBJECT) return UA_STATUSCODE_BADNODECLASSINVALID; // copy the variable attributes UA_ObjectAttributes attr; UA_ObjectAttributes_init(&attr); UA_LocalizedText_copy(&node->displayName, &attr.displayName); UA_LocalizedText_copy(&node->description, &attr.description); attr.writeMask = node->writeMask; attr.userWriteMask = node->userWriteMask; attr.eventNotifier = node->eventNotifier; UA_AddNodesItem item; UA_AddNodesItem_init(&item); UA_NodeId_copy(parent, &item.parentNodeId.nodeId); UA_NodeId_copy(referenceType, &item.referenceTypeId); UA_QualifiedName_copy(&node->browseName, &item.browseName); item.nodeClass = UA_NODECLASS_OBJECT; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES]; item.nodeAttributes.content.decoded.data = &attr; // don't add a typedefinition here. // add the new object UA_AddNodesResult res; UA_AddNodesResult_init(&res); Service_AddNodes_single(server, session, &item, &res, instantiationCallback); UA_ObjectAttributes_deleteMembers(&attr); UA_AddNodesItem_deleteMembers(&item); // now instantiate the object for all hastypedefinition references for(size_t i = 0; i < node->referencesSize; i++) { UA_ReferenceNode *rn = &node->references[i]; if(rn->isInverse) continue; const UA_NodeId hasTypeDef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); if(!UA_NodeId_equal(&rn->referenceTypeId, &hasTypeDef)) continue; instantiateObjectNode(server, session, &res.addedNodeId, &rn->targetId.nodeId, instantiationCallback); } if (instantiationCallback != NULL) instantiationCallback->method(res.addedNodeId, node->nodeId, instantiationCallback->handle); UA_AddNodesResult_deleteMembers(&res); return UA_STATUSCODE_GOOD; }
UA_StatusCode UA_EXPORT UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_QualifiedName browseName, const UA_MethodAttributes attr, UA_MethodCallback method, void *handle, UA_Int32 inputArgumentsSize, const UA_Argument* inputArguments, UA_Int32 outputArgumentsSize, const UA_Argument* outputArguments, UA_NodeId *outNewNodeId) { UA_AddNodesResult result; UA_AddNodesResult_init(&result); UA_AddNodesItem item; UA_AddNodesItem_init(&item); result.statusCode = UA_QualifiedName_copy(&browseName, &item.browseName); item.nodeClass = UA_NODECLASS_METHOD; result.statusCode |= UA_NodeId_copy(&parentNodeId, &item.parentNodeId.nodeId); result.statusCode |= UA_NodeId_copy(&referenceTypeId, &item.referenceTypeId); result.statusCode |= UA_NodeId_copy(&requestedNewNodeId, &item.requestedNewNodeId.nodeId); UA_MethodAttributes attrCopy; result.statusCode |= UA_MethodAttributes_copy(&attr, &attrCopy); if(result.statusCode != UA_STATUSCODE_GOOD) { UA_AddNodesItem_deleteMembers(&item); UA_MethodAttributes_deleteMembers(&attrCopy); return result.statusCode; } UA_MethodNode *node = UA_MethodNode_new(); if(!node) { result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY; UA_AddNodesItem_deleteMembers(&item); UA_MethodAttributes_deleteMembers(&attrCopy); return result.statusCode; } copyStandardAttributes((UA_Node*)node, &item, (UA_NodeAttributes*)&attrCopy); node->executable = attrCopy.executable; node->userExecutable = attrCopy.executable; node->attachedMethod = method; node->methodHandle = handle; UA_AddNodesItem_deleteMembers(&item); UA_MethodAttributes_deleteMembers(&attrCopy); UA_Server_addExistingNode(server, &adminSession, (UA_Node*)node, &item.parentNodeId.nodeId, &item.referenceTypeId, &result); if(result.statusCode != UA_STATUSCODE_GOOD) { UA_MethodNode_delete(node); return result.statusCode; } UA_ExpandedNodeId parent; UA_ExpandedNodeId_init(&parent); parent.nodeId = result.addedNodeId; /* create InputArguments */ UA_VariableNode *inputArgumentsVariableNode = UA_VariableNode_new(); inputArgumentsVariableNode->nodeId.namespaceIndex = result.addedNodeId.namespaceIndex; inputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0,"InputArguments"); inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments"); inputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments"); inputArgumentsVariableNode->valueRank = 1; UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.variant.value, inputArguments, inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]); UA_AddNodesResult inputAddRes; const UA_NodeId hasproperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); UA_Server_addExistingNode(server, &adminSession, (UA_Node*)inputArgumentsVariableNode, &parent.nodeId, &hasproperty, &inputAddRes); // todo: check if adding succeeded UA_AddNodesResult_deleteMembers(&inputAddRes); /* create OutputArguments */ UA_VariableNode *outputArgumentsVariableNode = UA_VariableNode_new(); outputArgumentsVariableNode->nodeId.namespaceIndex = result.addedNodeId.namespaceIndex; outputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0,"OutputArguments"); outputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments"); outputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments"); outputArgumentsVariableNode->valueRank = 1; UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.variant.value, outputArguments, outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]); UA_AddNodesResult outputAddRes; UA_Server_addExistingNode(server, &adminSession, (UA_Node*)outputArgumentsVariableNode, &parent.nodeId, &hasproperty, &outputAddRes); // todo: check if adding succeeded UA_AddNodesResult_deleteMembers(&outputAddRes); if(outNewNodeId) *outNewNodeId = result.addedNodeId; else UA_AddNodesResult_deleteMembers(&result); return result.statusCode; }
UA_StatusCode UA_EXPORT UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_QualifiedName browseName, const UA_MethodAttributes attr, UA_MethodCallback method, void *handle, size_t inputArgumentsSize, const UA_Argument* inputArguments, size_t outputArgumentsSize, const UA_Argument* outputArguments, UA_NodeId *outNewNodeId) { UA_AddNodesResult result; UA_AddNodesResult_init(&result); UA_AddNodesItem item; UA_AddNodesItem_init(&item); result.statusCode = UA_QualifiedName_copy(&browseName, &item.browseName); item.nodeClass = UA_NODECLASS_METHOD; result.statusCode |= UA_NodeId_copy(&parentNodeId, &item.parentNodeId.nodeId); result.statusCode |= UA_NodeId_copy(&referenceTypeId, &item.referenceTypeId); result.statusCode |= UA_NodeId_copy(&requestedNewNodeId, &item.requestedNewNodeId.nodeId); UA_MethodAttributes attrCopy; result.statusCode |= UA_MethodAttributes_copy(&attr, &attrCopy); if(result.statusCode != UA_STATUSCODE_GOOD) { UA_AddNodesItem_deleteMembers(&item); UA_MethodAttributes_deleteMembers(&attrCopy); return result.statusCode; } UA_MethodNode *node = UA_NodeStore_newMethodNode(); if(!node) { result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY; UA_AddNodesItem_deleteMembers(&item); UA_MethodAttributes_deleteMembers(&attrCopy); return result.statusCode; } copyStandardAttributes((UA_Node*)node, &item, (UA_NodeAttributes*)&attrCopy); node->executable = attrCopy.executable; node->userExecutable = attrCopy.executable; node->attachedMethod = method; node->methodHandle = handle; UA_AddNodesItem_deleteMembers(&item); UA_MethodAttributes_deleteMembers(&attrCopy); UA_RCU_LOCK(); UA_Server_addExistingNode(server, &adminSession, (UA_Node*)node, &item.parentNodeId.nodeId, &item.referenceTypeId, &result); UA_RCU_UNLOCK(); if(result.statusCode != UA_STATUSCODE_GOOD) return result.statusCode; UA_ExpandedNodeId parent; UA_ExpandedNodeId_init(&parent); parent.nodeId = result.addedNodeId; /* create InputArguments */ /* FIXME: The namespace compiler does currently not recognize the interdependancy between methods * and arguments - everything is treated as a standalone node. * In order to allow the compiler to create methods with arguments from an XML, this routine * must be coerced into *not* creating arguments, as these will be added by the compiler as * standalone nodes later. A semantic of inputArgumentsSize < 0 is used to signal this. * * This is not a production feature and should be fixed on the compiler side! (@ichrispa) */ const UA_NodeId hasproperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); UA_VariableNode *inputArgumentsVariableNode = UA_NodeStore_newVariableNode(); inputArgumentsVariableNode->nodeId.namespaceIndex = result.addedNodeId.namespaceIndex; inputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0,"InputArguments"); inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments"); inputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments"); inputArgumentsVariableNode->valueRank = 1; UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.variant.value, inputArguments, inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]); UA_AddNodesResult inputAddRes; UA_Server_addExistingNode(server, &adminSession, (UA_Node*)inputArgumentsVariableNode, &parent.nodeId, &hasproperty, &inputAddRes); // todo: check if adding succeeded UA_AddNodesResult_deleteMembers(&inputAddRes); /* create OutputArguments */ /* FIXME: See comment in inputArguments */ UA_VariableNode *outputArgumentsVariableNode = UA_NodeStore_newVariableNode(); outputArgumentsVariableNode->nodeId.namespaceIndex = result.addedNodeId.namespaceIndex; outputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0,"OutputArguments"); outputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments"); outputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments"); outputArgumentsVariableNode->valueRank = 1; UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.variant.value, outputArguments, outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]); UA_AddNodesResult outputAddRes; UA_Server_addExistingNode(server, &adminSession, (UA_Node*)outputArgumentsVariableNode, &parent.nodeId, &hasproperty, &outputAddRes); // todo: check if adding succeeded UA_AddNodesResult_deleteMembers(&outputAddRes); if(outNewNodeId) *outNewNodeId = result.addedNodeId; // don't deleteMember the result else UA_AddNodesResult_deleteMembers(&result); return result.statusCode; }