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; }
static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node **new_node) { if(attributes->typeId.identifier.numeric != UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES].typeId.identifier.numeric + UA_ENCODINGOFFSET_BINARY) return UA_STATUSCODE_BADNODEATTRIBUTESINVALID; UA_VariableAttributes attr; size_t pos = 0; // todo return more informative error codes from decodeBinary if(UA_VariableAttributes_decodeBinary(&attributes->body, &pos, &attr) != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADNODEATTRIBUTESINVALID; UA_VariableNode *vnode = UA_VariableNode_new(); if(!vnode) { UA_VariableAttributes_deleteMembers(&attr); return UA_STATUSCODE_BADOUTOFMEMORY; } // now copy all the attributes. This potentially removes them from the decoded attributes. COPY_STANDARDATTRIBUTES; if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ACCESSLEVEL) vnode->accessLevel = attr.accessLevel; if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_USERACCESSLEVEL) vnode->userAccessLevel = attr.userAccessLevel; if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_HISTORIZING) vnode->historizing = attr.historizing; if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_MINIMUMSAMPLINGINTERVAL) vnode->minimumSamplingInterval = attr.minimumSamplingInterval; if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_VALUERANK) vnode->valueRank = attr.valueRank; // don't use extra dimension spec. This comes from the value. /* if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ARRAYDIMENSIONS) { */ /* vnode->arrayDimensionsSize = attr.arrayDimensionsSize; */ /* vnode->arrayDimensions = attr.arrayDimensions; */ /* attr.arrayDimensionsSize = -1; */ /* attr.arrayDimensions = UA_NULL; */ /* } */ // don't use the extra type id. This comes from the value. /* if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DATATYPE || */ /* attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_OBJECTTYPEORDATATYPE) { */ /* vnode->dataType = attr.dataType; */ /* UA_NodeId_init(&attr.dataType); */ /* } */ if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_VALUE) { vnode->value.variant = attr.value; UA_Variant_init(&attr.value); } UA_VariableAttributes_deleteMembers(&attr); *new_node = (UA_Node*)vnode; return UA_STATUSCODE_GOOD; }
static UA_Node * variableNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableAttributes *attr) { UA_VariableNode *vnode = UA_VariableNode_new(); if(!vnode) return NULL; UA_StatusCode retval = copyStandardAttributes((UA_Node*)vnode, item, (const UA_NodeAttributes*)attr); // todo: test if the type / valueRank / value attributes are consistent vnode->accessLevel = attr->accessLevel; vnode->userAccessLevel = attr->userAccessLevel; vnode->historizing = attr->historizing; vnode->minimumSamplingInterval = attr->minimumSamplingInterval; vnode->valueRank = attr->valueRank; retval |= UA_Variant_copy(&attr->value, &vnode->value.variant.value); if(retval != UA_STATUSCODE_GOOD) { UA_Node_deleteAnyNodeClass((UA_Node*)vnode); return NULL; } return (UA_Node*)vnode; }
static UA_VariableNode* makeCompareSequence(void) { UA_VariableNode *node = UA_VariableNode_new(); UA_Variant *myIntegerVariant = UA_Variant_new(); UA_Int32 myInteger = 42; UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]); const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer"); const UA_LocalizedText myIntegerDisplName = UA_LOCALIZEDTEXT("en_US", "the answer"); const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); //UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES); node->value.variant.value = *myIntegerVariant; UA_NodeId_copy(&myIntegerNodeId,&node->nodeId); UA_QualifiedName_copy(&myIntegerName,&node->browseName); UA_LocalizedText_copy(&myIntegerDisplName, &node->displayName); UA_LocalizedText_copy(&myIntegerDisplName, &node->description); UA_ExpandedNodeId parentId; UA_ExpandedNodeId_init(&parentId); UA_NodeId_copy(&parentNodeId,&parentId.nodeId); return node; }
UA_StatusCode UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource, const UA_QualifiedName browseName, UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId) { UA_VariableNode *node = UA_VariableNode_new(); node->valueSource = UA_VALUESOURCE_DATASOURCE; node->value.dataSource = dataSource; UA_NodeId_copy(&nodeId, &node->nodeId); UA_QualifiedName_copy(&browseName, &node->browseName); UA_String_copy(&browseName.name, &node->displayName.text); UA_ExpandedNodeId parentId; // dummy exapndednodeid UA_ExpandedNodeId_init(&parentId); UA_NodeId_copy(&parentNodeId, &parentId.nodeId); UA_AddNodesResult res = UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, parentId, referenceTypeId); ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE)); if(res.statusCode != UA_STATUSCODE_GOOD) UA_VariableNode_delete(node); UA_AddNodesResult_deleteMembers(&res); return res.statusCode; }
UA_StatusCode UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_QualifiedName browseName, UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId) { UA_VariableNode *node = UA_VariableNode_new(); node->value.variant = *value; // copy content UA_NodeId_copy(&nodeId, &node->nodeId); UA_QualifiedName_copy(&browseName, &node->browseName); UA_String_copy(&browseName.name, &node->displayName.text); UA_ExpandedNodeId parentId; // we need an expandednodeid UA_ExpandedNodeId_init(&parentId); UA_NodeId_copy(&parentNodeId, &parentId.nodeId); UA_AddNodesResult res = UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, parentId, referenceTypeId); ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), UA_EXPANDEDNODEID_NUMERIC(0, value->type->typeId.identifier.numeric)); if(res.statusCode != UA_STATUSCODE_GOOD) { UA_Variant_init(&node->value.variant); UA_VariableNode_delete(node); } else UA_free(value); UA_AddNodesResult_deleteMembers(&res); return res.statusCode; }
static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) { UA_StatusCode retval = UA_STATUSCODE_GOOD; /* is there a value at all */ if(!wvalue->value.hasValue) return UA_STATUSCODE_BADTYPEMISMATCH; // we might repeat writing, e.g. when the node got replaced mid-work UA_Boolean done = UA_FALSE; while(!done) { const UA_Node *node = UA_NodeStore_get(server->nodestore, &wvalue->nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; switch(wvalue->attributeId) { case UA_ATTRIBUTEID_NODEID: case UA_ATTRIBUTEID_NODECLASS: case UA_ATTRIBUTEID_BROWSENAME: case UA_ATTRIBUTEID_DISPLAYNAME: case UA_ATTRIBUTEID_DESCRIPTION: case UA_ATTRIBUTEID_WRITEMASK: case UA_ATTRIBUTEID_USERWRITEMASK: case UA_ATTRIBUTEID_ISABSTRACT: case UA_ATTRIBUTEID_SYMMETRIC: case UA_ATTRIBUTEID_INVERSENAME: case UA_ATTRIBUTEID_CONTAINSNOLOOPS: case UA_ATTRIBUTEID_EVENTNOTIFIER: retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_VALUE: { if(node->nodeClass != UA_NODECLASS_VARIABLE && node->nodeClass != UA_NODECLASS_VARIABLETYPE) { retval = UA_STATUSCODE_BADTYPEMISMATCH; break; } /* parse the range */ UA_Boolean hasRange = UA_FALSE; UA_NumericRange range; if(wvalue->indexRange.length > 0) { retval = parse_numericrange(wvalue->indexRange, &range); if(retval != UA_STATUSCODE_GOOD) break; hasRange = UA_TRUE; } /* the relevant members are similar for variables and variabletypes */ const UA_VariableNode *vn = (const UA_VariableNode*)node; if(vn->valueSource == UA_VALUESOURCE_DATASOURCE) { if(!vn->value.dataSource.write) { retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; } // todo: writing ranges retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value); done = UA_TRUE; break; } const UA_Variant *oldV = &vn->value.variant; /* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */ if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) { if(oldV->type->namespaceZero && wvalue->value.value.type->namespaceZero && oldV->type->typeIndex == wvalue->value.value.type->typeIndex) /* An enum was sent as an int32, or an opaque type as a bytestring. This is detected with the typeIndex indicated the "true" datatype. */ wvalue->value.value.type = oldV->type; else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && (!oldV->data || vn->value.variant.arrayLength > -1) /* isArray */ && wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] && wvalue->value.value.data && wvalue->value.value.arrayLength == -1 /* isScalar */) { /* a string is written to a byte array */ UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data; wvalue->value.value.arrayLength = str->length; wvalue->value.value.data = str->data; wvalue->value.value.type = &UA_TYPES[UA_TYPES_BYTE]; UA_free(str); } else { retval = UA_STATUSCODE_BADTYPEMISMATCH; break; } } /* copy the node */ UA_VariableNode *newVn = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_new() : (UA_VariableNode*)UA_VariableTypeNode_new(); if(!newVn) { retval = UA_STATUSCODE_BADOUTOFMEMORY; break; } retval = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_copy(vn, newVn) : UA_VariableTypeNode_copy((const UA_VariableTypeNode*)vn, (UA_VariableTypeNode*)newVn); if(retval != UA_STATUSCODE_GOOD) goto clean_up; /* insert the new value*/ if(hasRange) retval = UA_Variant_setRange(&newVn->value.variant, wvalue->value.value.data, range); else { UA_Variant_deleteMembers(&newVn->value.variant); retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant); } if(retval != UA_STATUSCODE_GOOD || UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn, UA_NULL) != UA_STATUSCODE_GOOD) goto clean_up; if(hasRange) UA_free(range.dimensions); done = UA_TRUE; break; clean_up: if(node->nodeClass == UA_NODECLASS_VARIABLE) UA_VariableNode_delete(newVn); else UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn); if(hasRange) UA_free(range.dimensions); } break; case UA_ATTRIBUTEID_DATATYPE: case UA_ATTRIBUTEID_VALUERANK: case UA_ATTRIBUTEID_ARRAYDIMENSIONS: case UA_ATTRIBUTEID_ACCESSLEVEL: case UA_ATTRIBUTEID_USERACCESSLEVEL: case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: case UA_ATTRIBUTEID_HISTORIZING: case UA_ATTRIBUTEID_EXECUTABLE: case UA_ATTRIBUTEID_USEREXECUTABLE: retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); if(retval != UA_STATUSCODE_GOOD) break; } return retval; }
/* Adds a one-way reference to the local nodestore */ static UA_StatusCode addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item) { const UA_Node *node = UA_NodeStore_get(server->nodestore, &item->sourceNodeId); if(!node) return UA_STATUSCODE_BADINTERNALERROR; UA_StatusCode retval = UA_STATUSCODE_GOOD; #ifndef UA_MULTITHREADING size_t i = node->referencesSize; if(node->referencesSize < 0) i = 0; size_t refssize = (i+1) | 3; // so the realloc is not necessary every time UA_ReferenceNode *new_refs = UA_realloc(node->references, sizeof(UA_ReferenceNode) * refssize); if(!new_refs) retval = UA_STATUSCODE_BADOUTOFMEMORY; else { UA_ReferenceNode_init(&new_refs[i]); retval = UA_NodeId_copy(&item->referenceTypeId, &new_refs[i].referenceTypeId); new_refs[i].isInverse = !item->isForward; retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[i].targetId); /* hack. be careful! possible only in the single-threaded case. */ UA_Node *mutable_node = (UA_Node*)(uintptr_t)node; mutable_node->references = new_refs; if(retval != UA_STATUSCODE_GOOD) { UA_NodeId_deleteMembers(&new_refs[node->referencesSize].referenceTypeId); UA_ExpandedNodeId_deleteMembers(&new_refs[node->referencesSize].targetId); } else mutable_node->referencesSize = i+1; } UA_NodeStore_release(node); return retval; #else UA_Node *newNode = UA_NULL; void (*deleteNode)(UA_Node*) = UA_NULL; switch(node->nodeClass) { case UA_NODECLASS_OBJECT: newNode = (UA_Node*)UA_ObjectNode_new(); UA_ObjectNode_copy((const UA_ObjectNode*)node, (UA_ObjectNode*)newNode); deleteNode = (void (*)(UA_Node*))UA_ObjectNode_delete; break; case UA_NODECLASS_VARIABLE: newNode = (UA_Node*)UA_VariableNode_new(); UA_VariableNode_copy((const UA_VariableNode*)node, (UA_VariableNode*)newNode); deleteNode = (void (*)(UA_Node*))UA_VariableNode_delete; break; case UA_NODECLASS_METHOD: newNode = (UA_Node*)UA_MethodNode_new(); UA_MethodNode_copy((const UA_MethodNode*)node, (UA_MethodNode*)newNode); deleteNode = (void (*)(UA_Node*))UA_MethodNode_delete; break; case UA_NODECLASS_OBJECTTYPE: newNode = (UA_Node*)UA_ObjectTypeNode_new(); UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)node, (UA_ObjectTypeNode*)newNode); deleteNode = (void (*)(UA_Node*))UA_ObjectTypeNode_delete; break; case UA_NODECLASS_VARIABLETYPE: newNode = (UA_Node*)UA_VariableTypeNode_new(); UA_VariableTypeNode_copy((const UA_VariableTypeNode*)node, (UA_VariableTypeNode*)newNode); deleteNode = (void (*)(UA_Node*))UA_VariableTypeNode_delete; break; case UA_NODECLASS_REFERENCETYPE: newNode = (UA_Node*)UA_ReferenceTypeNode_new(); UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)node, (UA_ReferenceTypeNode*)newNode); deleteNode = (void (*)(UA_Node*))UA_ReferenceTypeNode_delete; break; case UA_NODECLASS_DATATYPE: newNode = (UA_Node*)UA_DataTypeNode_new(); UA_DataTypeNode_copy((const UA_DataTypeNode*)node, (UA_DataTypeNode*)newNode); deleteNode = (void (*)(UA_Node*))UA_DataTypeNode_delete; break; case UA_NODECLASS_VIEW: newNode = (UA_Node*)UA_ViewNode_new(); UA_ViewNode_copy((const UA_ViewNode*)node, (UA_ViewNode*)newNode); deleteNode = (void (*)(UA_Node*))UA_ViewNode_delete; break; default: return UA_STATUSCODE_BADINTERNALERROR; } UA_Int32 count = node->referencesSize; if(count < 0) count = 0; UA_ReferenceNode *old_refs = newNode->references; UA_ReferenceNode *new_refs = UA_malloc(sizeof(UA_ReferenceNode)*(count+1)); if(!new_refs) { deleteNode(newNode); UA_NodeStore_release(node); return UA_STATUSCODE_BADOUTOFMEMORY; } // insert the new reference UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode)*count); UA_ReferenceNode_init(&new_refs[count]); retval = UA_NodeId_copy(&item->referenceTypeId, &new_refs[count].referenceTypeId); new_refs[count].isInverse = !item->isForward; retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[count].targetId); if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(new_refs, &UA_TYPES[UA_TYPES_REFERENCENODE], ++count); newNode->references = UA_NULL; newNode->referencesSize = 0; deleteNode(newNode); UA_NodeStore_release(node); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_free(old_refs); newNode->references = new_refs; newNode->referencesSize = ++count; retval = UA_NodeStore_replace(server->nodestore, node, newNode, UA_NULL); UA_NodeStore_release(node); if (retval == UA_STATUSCODE_BADINTERNALERROR) { /* presumably because the node was replaced and an old version was updated at the same time. just try again */ deleteNode(newNode); return addOneWayReferenceWithSession(server, session, item); } return retval; #endif }
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; }