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; }
/* 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; }