UA_StatusCode UA_Server_addObjectNode(UA_Server *server, const UA_QualifiedName browseName, UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_NodeId typeDefinition) { UA_ObjectNode *node = UA_ObjectNode_new(); 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); if(res.statusCode != UA_STATUSCODE_GOOD) { UA_ObjectNode_delete(node); } UA_AddNodesResult_deleteMembers(&res); if(!(UA_NodeId_isNull(&typeDefinition))){ UA_ExpandedNodeId typeDefid; // we need an expandednodeid UA_ExpandedNodeId_init(&typeDefid); UA_NodeId_copy(&typeDefinition, &typeDefid.nodeId); ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), typeDefid); } return res.statusCode; }
END_TEST START_TEST(Nodes_createCustomObjectType) { // Create a custom object type "CustomDemoType" which has a "CustomStateType" component UA_StatusCode retval = UA_STATUSCODE_GOOD; /* create new object type node which has a subcomponent of the type StateType */ UA_ObjectTypeAttributes otAttr = UA_ObjectTypeAttributes_default; otAttr.displayName = UA_LOCALIZEDTEXT("", "CustomDemoType"); otAttr.description = UA_LOCALIZEDTEXT("", ""); retval = UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 6010), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(1, "CustomDemoType"), otAttr, NULL, NULL); ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD); UA_ObjectAttributes oAttr = UA_ObjectAttributes_default; oAttr.displayName = UA_LOCALIZEDTEXT("", "State"); oAttr.description = UA_LOCALIZEDTEXT("", ""); retval = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 6011), UA_NODEID_NUMERIC(1, 6010), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "State"), UA_NODEID_NUMERIC(1, 6000), oAttr, NULL, NULL); ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD); /* modelling rule is mandatory so it will be inherited for the object created from CustomDemoType */ retval = UA_Server_addReference(server, UA_NODEID_NUMERIC(1, 6011), UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true); ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD); /* assign a default value to the attribute "StateNumber" inside the state attribute (part of the MyDemoType) */ UA_Variant stateNum; UA_Variant_init(&stateNum); UA_Variant_setScalar(&stateNum, &valueToBeInherited, &UA_TYPES[UA_TYPES_UINT32]); UA_NodeId childID; findChildId(UA_NODEID_NUMERIC(1, 6011), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_QUALIFIEDNAME(1, "CustomStateNumber"), &childID); ck_assert(!UA_NodeId_isNull(&childID)); retval = UA_Server_writeValue(server, childID, stateNum); ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD); }
END_TEST START_TEST(Nodes_checkInheritedValue) { UA_NodeId childState; findChildId(UA_NODEID_NUMERIC(1, 6020), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "State"), &childState); ck_assert(!UA_NodeId_isNull(&childState)); UA_NodeId childNumber; findChildId(childState, UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_QUALIFIEDNAME(1, "CustomStateNumber"), &childNumber); ck_assert(!UA_NodeId_isNull(&childNumber)); UA_Variant inheritedValue; UA_Variant_init(&inheritedValue); UA_Server_readValue(server, childNumber, &inheritedValue); ck_assert(inheritedValue.type == &UA_TYPES[UA_TYPES_UINT32]); UA_UInt32 *value = (UA_UInt32 *) inheritedValue.data; ck_assert_int_eq(*value, valueToBeInherited); UA_Variant_deleteMembers(&inheritedValue); }
UA_Boolean compatibleDataType(UA_Server *server, const UA_NodeId *dataType, const UA_NodeId *constraintDataType) { /* Do not allow empty datatypes */ if(UA_NodeId_isNull(dataType)) return false; /* No constraint (TODO: use variant instead) */ if(UA_NodeId_isNull(constraintDataType)) return true; /* Variant allows any subtype */ if(UA_NodeId_equal(constraintDataType, &UA_TYPES[UA_TYPES_VARIANT].typeId)) return true; /* Is the value-type a subtype of the required type? */ if(isNodeInTree(&server->config.nodestore, dataType, constraintDataType, &subtypeId, 1)) return true; /* If value is a built-in type: The target data type may be a sub type of * the built-in type. (e.g. UtcTime is sub-type of DateTime and has a * DateTime value). A type is builtin if its NodeId is in Namespace 0 and * has a numeric identifier <= 25 (DiagnosticInfo) */ if(dataType->namespaceIndex == 0 && dataType->identifierType == UA_NODEIDTYPE_NUMERIC && dataType->identifier.numeric <= 25 && isNodeInTree(&server->config.nodestore, constraintDataType, dataType, &subtypeId, 1)) return true; /* Enum allows Int32 (only) */ if(UA_NodeId_equal(dataType, &UA_TYPES[UA_TYPES_INT32].typeId) && isNodeInTree(&server->config.nodestore, constraintDataType, &enumNodeId, &subtypeId, 1)) return true; return false; }
UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, UA_Node **inserted) { if(ns->size * 3 <= ns->count * 4) { if(expand(ns) != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADINTERNALERROR; } UA_NodeStoreEntry *entry; UA_NodeId tempNodeid; tempNodeid = node->nodeId; tempNodeid.namespaceIndex = 0; if(UA_NodeId_isNull(&tempNodeid)) { /* find a free nodeid */ if(node->nodeId.namespaceIndex == 0) //original request for ns=0 should yield ns=1 node->nodeId.namespaceIndex = 1; UA_Int32 identifier = ns->count+1; // start value UA_Int32 size = ns->size; hash_t increase = mod2(identifier, size); while(UA_TRUE) { node->nodeId.identifier.numeric = identifier; if(!containsNodeId(ns, &node->nodeId, &entry)) break; identifier += increase; if(identifier >= size) identifier -= size; } } else { if(containsNodeId(ns, &node->nodeId, &entry)) return UA_STATUSCODE_BADNODEIDEXISTS; } fillEntry(entry, node); ns->count++; if(inserted) *inserted = &entry->node.node; return UA_STATUSCODE_GOOD; }
UA_AddNodesResult UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *node, const UA_ExpandedNodeId parentNodeId, const UA_NodeId referenceTypeId) { UA_AddNodesResult result; UA_AddNodesResult_init(&result); if(node->nodeId.namespaceIndex >= server->namespacesSize) { result.statusCode = UA_STATUSCODE_BADNODEIDINVALID; return result; } const UA_Node *parent = UA_NodeStore_get(server->nodestore, &parentNodeId.nodeId); if(!parent) { result.statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID; return result; } const UA_ReferenceTypeNode *referenceType = (const UA_ReferenceTypeNode *)UA_NodeStore_get(server->nodestore, &referenceTypeId); if(!referenceType) { result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; goto ret; } if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) { result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; goto ret2; } if(referenceType->isAbstract == UA_TRUE) { result.statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED; goto ret2; } // todo: test if the referencetype is hierarchical const UA_Node *managed = UA_NULL; if(UA_NodeId_isNull(&node->nodeId)) { if(UA_NodeStore_insert(server->nodestore, node, &managed) != UA_STATUSCODE_GOOD) { result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY; goto ret2; } result.addedNodeId = managed->nodeId; // cannot fail as unique nodeids are numeric } else { if(UA_NodeId_copy(&node->nodeId, &result.addedNodeId) != UA_STATUSCODE_GOOD) { result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY; goto ret2; } if(UA_NodeStore_insert(server->nodestore, node, &managed) != UA_STATUSCODE_GOOD) { result.statusCode = UA_STATUSCODE_BADNODEIDEXISTS; // todo: differentiate out of memory UA_NodeId_deleteMembers(&result.addedNodeId); goto ret2; } } // reference back to the parent UA_AddReferencesItem item; UA_AddReferencesItem_init(&item); item.sourceNodeId = managed->nodeId; item.referenceTypeId = referenceType->nodeId; item.isForward = UA_FALSE; item.targetNodeId.nodeId = parent->nodeId; UA_Server_addReference(server, &item); // todo: error handling. remove new node from nodestore UA_NodeStore_release(managed); ret2: UA_NodeStore_release((const UA_Node*)referenceType); ret: UA_NodeStore_release(parent); return result; }
void Service_AddNodes_single(UA_Server *server, UA_Session *session, const UA_AddNodesItem *item, UA_AddNodesResult *result) { if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED || !item->nodeAttributes.content.decoded.type) { result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; return; } /* create the node */ UA_Node *node; switch(item->nodeClass) { case UA_NODECLASS_OBJECT: if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES]) { result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; return; } node = objectNodeFromAttributes(item, item->nodeAttributes.content.decoded.data); break; case UA_NODECLASS_VARIABLE: if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES]) { result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; return; } node = variableNodeFromAttributes(item, item->nodeAttributes.content.decoded.data); break; case UA_NODECLASS_OBJECTTYPE: if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]) { result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; return; } node = objectTypeNodeFromAttributes(item, item->nodeAttributes.content.decoded.data); break; case UA_NODECLASS_VARIABLETYPE: if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]) { result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; return; } node = variableTypeNodeFromAttributes(item, item->nodeAttributes.content.decoded.data); break; case UA_NODECLASS_REFERENCETYPE: if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]) { result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; return; } node = referenceTypeNodeFromAttributes(item, item->nodeAttributes.content.decoded.data); break; case UA_NODECLASS_DATATYPE: if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES]) { result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; return; } node = dataTypeNodeFromAttributes(item, item->nodeAttributes.content.decoded.data); break; case UA_NODECLASS_VIEW: if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_VIEWATTRIBUTES]) { result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; return; } node = viewNodeFromAttributes(item, item->nodeAttributes.content.decoded.data); break; case UA_NODECLASS_METHOD: case UA_NODECLASS_UNSPECIFIED: default: result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; return; } if(!node) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } /* add it to the server */ UA_Server_addExistingNode(server, session, node, &item->parentNodeId.nodeId, &item->referenceTypeId, result); if(result->statusCode != UA_STATUSCODE_GOOD) { UA_Node_deleteAnyNodeClass(node); return; } /* instantiate if it has a type */ if(!UA_NodeId_isNull(&item->typeDefinition.nodeId)) { if(item->nodeClass == UA_NODECLASS_OBJECT) result->statusCode = instantiateObjectNode(server, session, &result->addedNodeId, &item->typeDefinition.nodeId); else if(item->nodeClass == UA_NODECLASS_VARIABLE) result->statusCode = instantiateVariableNode(server, session, &result->addedNodeId, &item->typeDefinition.nodeId); } /* if instantiation failed, remove the node */ if(result->statusCode != UA_STATUSCODE_GOOD) Service_DeleteNodes_single(server, session, &result->addedNodeId, UA_TRUE); }
void UA_Server_addExistingNode(UA_Server *server, UA_Session *session, UA_Node *node, const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId, UA_AddNodesResult *result) { if(node->nodeId.namespaceIndex >= server->namespacesSize) { result->statusCode = UA_STATUSCODE_BADNODEIDINVALID; return; } const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId); if(!parent) { result->statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID; return; } const UA_ReferenceTypeNode *referenceType = (const UA_ReferenceTypeNode *)UA_NodeStore_get(server->nodestore, referenceTypeId); if(!referenceType) { result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; return; } if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) { result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; return; } if(referenceType->isAbstract == UA_TRUE) { result->statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED; return; } // todo: test if the referencetype is hierarchical // todo: namespace index is assumed to be valid UA_MT_CONST UA_Node *managed = NULL; UA_NodeId tempNodeid = node->nodeId; tempNodeid.namespaceIndex = 0; if(UA_NodeId_isNull(&tempNodeid)) { if(UA_NodeStore_insert(server->nodestore, node, &managed) != UA_STATUSCODE_GOOD) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } result->addedNodeId = managed->nodeId; // cannot fail as unique nodeids are numeric } else { if(UA_NodeId_copy(&node->nodeId, &result->addedNodeId) != UA_STATUSCODE_GOOD) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } if(UA_NodeStore_insert(server->nodestore, node, &managed) != UA_STATUSCODE_GOOD) { result->statusCode = UA_STATUSCODE_BADNODEIDEXISTS; UA_NodeId_deleteMembers(&result->addedNodeId); return; } } /* Careful. The node is inserted. If the nodestore makes an expand, nodes change their address */ // reference back to the parent UA_AddReferencesItem item; UA_AddReferencesItem_init(&item); item.sourceNodeId = managed->nodeId; item.referenceTypeId = *referenceTypeId; item.isForward = UA_FALSE; item.targetNodeId.nodeId = *parentNodeId; Service_AddReferences_single(server, session, &item); // todo: error handling. remove new node from nodestore }
UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node **inserted) { size_t nodesize; /* Copy the node into the entry. Then reset the original node. It shall no longer be used. */ switch(node->nodeClass) { case UA_NODECLASS_OBJECT: nodesize = sizeof(UA_ObjectNode); break; case UA_NODECLASS_VARIABLE: nodesize = sizeof(UA_VariableNode); break; case UA_NODECLASS_METHOD: nodesize = sizeof(UA_MethodNode); break; case UA_NODECLASS_OBJECTTYPE: nodesize = sizeof(UA_ObjectTypeNode); break; case UA_NODECLASS_VARIABLETYPE: nodesize = sizeof(UA_VariableTypeNode); break; case UA_NODECLASS_REFERENCETYPE: nodesize = sizeof(UA_ReferenceTypeNode); break; case UA_NODECLASS_DATATYPE: nodesize = sizeof(UA_DataTypeNode); break; case UA_NODECLASS_VIEW: nodesize = sizeof(UA_ViewNode); break; default: return UA_STATUSCODE_BADINTERNALERROR; } struct nodeEntry *entry; if(!(entry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize))) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy((void*)&entry->node, node, nodesize); cds_lfht_node_init(&entry->htn); entry->refcount = ALIVE_BIT; if(inserted) // increase the counter before adding the node entry->refcount++; struct cds_lfht_node *result; //FIXME: a bit dirty workaround of preserving namespace //namespace index is assumed to be valid UA_NodeId tempNodeid; UA_NodeId_copy(&node->nodeId, &tempNodeid); tempNodeid.namespaceIndex = 0; if(!UA_NodeId_isNull(&tempNodeid)) { hash_t h = hash(&node->nodeId); rcu_read_lock(); result = cds_lfht_add_unique(ns->ht, h, compare, &entry->node.nodeId, &entry->htn); rcu_read_unlock(); /* If the nodeid exists already */ if(result != &entry->htn) { UA_free(entry); return UA_STATUSCODE_BADNODEIDEXISTS; } } else { /* create a unique nodeid */ ((UA_Node *)&entry->node)->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC; if(((UA_Node *)&entry->node)->nodeId.namespaceIndex == 0) //original request for ns=0 should yield ns=1 ((UA_Node *)&entry->node)->nodeId.namespaceIndex = 1; if(((UA_Node *)&entry->node)->nodeClass==UA_NODECLASS_VARIABLE){ //set namespaceIndex in browseName in case id is generated ((UA_VariableNode*)&entry->node)->browseName.namespaceIndex=((UA_Node *)&entry->node)->nodeId.namespaceIndex; } unsigned long identifier; long before, after; rcu_read_lock(); cds_lfht_count_nodes(ns->ht, &before, &identifier, &after); // current amount of nodes stored identifier++; ((UA_Node *)&entry->node)->nodeId.identifier.numeric = identifier; while(UA_TRUE) { hash_t nhash = hash(&entry->node.nodeId); result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn); if(result == &entry->htn) break; ((UA_Node *)&entry->node)->nodeId.identifier.numeric += (identifier * 2654435761); } rcu_read_unlock(); } UA_NodeId_deleteMembers(&tempNodeid); UA_free(node); if(inserted) *inserted = &entry->node; return UA_STATUSCODE_GOOD; }