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; UA_NodeStore_deleteNode(node); return; } const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId); if(!parent) { result->statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID; UA_NodeStore_deleteNode(node); return; } const UA_ReferenceTypeNode *referenceType = (const UA_ReferenceTypeNode *)UA_NodeStore_get(server->nodestore, referenceTypeId); if(!referenceType) { result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; UA_NodeStore_deleteNode(node); return; } if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) { result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; UA_NodeStore_deleteNode(node); return; } if(referenceType->isAbstract == UA_TRUE) { result->statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED; UA_NodeStore_deleteNode(node); return; } // todo: test if the referencetype is hierarchical // todo: namespace index is assumed to be valid result->statusCode = UA_NodeStore_insert(server->nodestore, node); if(result->statusCode == UA_STATUSCODE_GOOD) result->statusCode = UA_NodeId_copy(&node->nodeId, &result->addedNodeId); else return; // reference back to the parent UA_AddReferencesItem item; UA_AddReferencesItem_init(&item); item.sourceNodeId = node->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 Service_DeleteNodes_single(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, UA_Boolean deleteReferences) { UA_MT_CONST UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDINVALID; if(deleteReferences == UA_TRUE) { UA_DeleteReferencesItem delItem; UA_DeleteReferencesItem_init(&delItem); delItem.deleteBidirectional = UA_FALSE; delItem.targetNodeId.nodeId = *nodeId; for(size_t i = 0; i < node->referencesSize; i++) { delItem.sourceNodeId = node->references[i].targetId.nodeId; delItem.isForward = node->references[i].isInverse; Service_DeleteReferences_single(server, session, &delItem); } } /* destroy an object before removing it */ if(node->nodeClass == UA_NODECLASS_OBJECT) { /* find the object type(s) */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.browseDirection = UA_BROWSEDIRECTION_INVERSE; bd.nodeId = *nodeId; bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE); bd.includeSubtypes = UA_TRUE; bd.nodeClassMask = UA_NODECLASS_OBJECTTYPE; /* browse type definitions with admin rights */ UA_BrowseResult result; UA_BrowseResult_init(&result); Service_Browse_single(server, &adminSession, NULL, &bd, UA_UINT32_MAX, &result); for(size_t i = 0; i < result.referencesSize; i++) { /* call the destructor */ UA_ReferenceDescription *rd = &result.references[i]; const UA_ObjectTypeNode *type = (const UA_ObjectTypeNode*)UA_NodeStore_get(server->nodestore, &rd->nodeId.nodeId); if(!type) continue; if(type->nodeClass != UA_NODECLASS_OBJECTTYPE || !type->lifecycleManagement.destructor) continue; /* if there are several types with lifecycle management, call all the destructors */ type->lifecycleManagement.destructor(*nodeId, ((const UA_ObjectNode*)node)->instanceHandle); } UA_BrowseResult_deleteMembers(&result); } return UA_NodeStore_remove(server->nodestore, nodeId); }
static UA_StatusCode instantiateObjectNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_NodeId *typeId, UA_InstantiationCallback *instantiationCallback) { const UA_ObjectTypeNode *typenode = (const UA_ObjectTypeNode*)UA_NodeStore_get(server->nodestore, typeId); if(!typenode) return UA_STATUSCODE_BADNODEIDINVALID; if(typenode->nodeClass != UA_NODECLASS_OBJECTTYPE) return UA_STATUSCODE_BADNODECLASSINVALID; /* Add all the child nodes */ UA_BrowseDescription browseChildren; UA_BrowseDescription_init(&browseChildren); browseChildren.nodeId = *typeId; browseChildren.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES); browseChildren.includeSubtypes = UA_TRUE; browseChildren.browseDirection = UA_BROWSEDIRECTION_FORWARD; browseChildren.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD; browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS; UA_BrowseResult browseResult; UA_BrowseResult_init(&browseResult); // todo: continuation points if there are too many results Service_Browse_single(server, session, NULL, &browseChildren, 100, &browseResult); for(size_t i = 0; i < browseResult.referencesSize; i++) { UA_ReferenceDescription *rd = &browseResult.references[i]; if(rd->nodeClass == UA_NODECLASS_METHOD) { /* add a reference to the method in the objecttype */ UA_AddReferencesItem item; UA_AddReferencesItem_init(&item); item.sourceNodeId = *nodeId; item.referenceTypeId = rd->referenceTypeId; item.isForward = UA_TRUE; item.targetNodeId = rd->nodeId; item.targetNodeClass = UA_NODECLASS_METHOD; Service_AddReferences_single(server, session, &item); } else if(rd->nodeClass == UA_NODECLASS_VARIABLE) copyExistingVariable(server, session, &rd->nodeId.nodeId, &rd->referenceTypeId, nodeId, instantiationCallback); else if(rd->nodeClass == UA_NODECLASS_OBJECT) copyExistingObject(server, session, &rd->nodeId.nodeId, &rd->referenceTypeId, nodeId, instantiationCallback); } /* add a hastypedefinition reference */ UA_AddReferencesItem addref; UA_AddReferencesItem_init(&addref); addref.sourceNodeId = *nodeId; addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); addref.isForward = UA_TRUE; addref.targetNodeId.nodeId = *typeId; addref.targetNodeClass = UA_NODECLASS_OBJECTTYPE; Service_AddReferences_single(server, session, &addref); /* call the constructor */ const UA_ObjectLifecycleManagement *olm = &typenode->lifecycleManagement; if(olm->constructor) UA_Server_editNode(server, session, nodeId, (UA_EditNodeCallback)setObjectInstanceHandle, olm->constructor(*nodeId)); return UA_STATUSCODE_GOOD; }
UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, UA_EditNodeCallback callback, const void *data) { UA_StatusCode retval; do { #ifndef UA_ENABLE_MULTITHREADING const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; UA_Node *editNode = (UA_Node*)(uintptr_t)node; // dirty cast. use only here. retval = callback(server, session, editNode, data); return retval; #else UA_Node *copy = UA_NodeStore_getCopy(server->nodestore, nodeId); if(!copy) return UA_STATUSCODE_BADOUTOFMEMORY; retval = callback(server, session, copy, data); if(retval != UA_STATUSCODE_GOOD) { UA_NodeStore_deleteNode(copy); return retval; } retval = UA_NodeStore_replace(server->nodestore, copy); #endif } while(retval != UA_STATUSCODE_GOOD); 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; }
/* 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; }
static UA_StatusCode instantiateVariableNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_NodeId *typeId) { const UA_ObjectTypeNode *type = (const UA_ObjectTypeNode*)UA_NodeStore_get(server->nodestore, typeId); if(!type) return UA_STATUSCODE_BADNODEIDINVALID; if(type->nodeClass != UA_NODECLASS_VARIABLETYPE) return UA_STATUSCODE_BADNODECLASSINVALID; /* get the references to child properties */ UA_BrowseDescription browseChildren; UA_BrowseDescription_init(&browseChildren); browseChildren.nodeId = *typeId; browseChildren.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); browseChildren.includeSubtypes = UA_TRUE; browseChildren.browseDirection = UA_BROWSEDIRECTION_FORWARD; browseChildren.nodeClassMask = UA_NODECLASS_VARIABLE; browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS; UA_BrowseResult browseResult; UA_BrowseResult_init(&browseResult); // todo: continuation points if there are too many results Service_Browse_single(server, session, NULL, &browseChildren, 100, &browseResult); /* add the child properties */ for(size_t i = 0; i < browseResult.referencesSize; i++) { UA_ReferenceDescription *rd = &browseResult.references[i]; copyExistingVariable(server, session, &rd->nodeId.nodeId, &rd->referenceTypeId, nodeId); } /* add a hastypedefinition reference */ UA_AddReferencesItem addref; UA_AddReferencesItem_init(&addref); addref.sourceNodeId = *nodeId; addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); addref.isForward = UA_TRUE; addref.targetNodeId.nodeId = *typeId; addref.targetNodeClass = UA_NODECLASS_OBJECTTYPE; Service_AddReferences_single(server, session, &addref); return UA_STATUSCODE_GOOD; }
static const UA_VariableNode * getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod, UA_String withBrowseName) { UA_NodeId hasProperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); for(size_t i = 0; i < ofMethod->referencesSize; i++) { if(ofMethod->references[i].isInverse == false && UA_NodeId_equal(&hasProperty, &ofMethod->references[i].referenceTypeId)) { const UA_Node *refTarget = UA_NodeStore_get(server->nodestore, &ofMethod->references[i].targetId.nodeId); if(!refTarget) continue; if(refTarget->nodeClass == UA_NODECLASS_VARIABLE && refTarget->browseName.namespaceIndex == 0 && UA_String_equal(&withBrowseName, &refTarget->browseName.name)) { return (const UA_VariableNode*) refTarget; } } } return NULL; }
}END_TEST START_TEST(WriteSingleAttributeValue) { UA_Server *server = makeTestSequence(); UA_WriteValue wValue; UA_WriteValue_init(&wValue); UA_Variant *myIntegerVariant = UA_Variant_new(); UA_Int32 myInteger = 20; UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]); wValue.nodeId = UA_NODEID_STRING(1, "the.answer"); wValue.attributeId = UA_ATTRIBUTEID_VALUE; wValue.value.hasValue = UA_TRUE; wValue.value.value = *myIntegerVariant; UA_StatusCode retval = writeValue(server, &wValue); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); ck_assert(wValue.value.hasValue); const UA_Node *node = UA_NodeStore_get(server->nodestore, &wValue.nodeId); ck_assert_int_eq(node->nodeClass, UA_NODECLASS_VARIABLE); const UA_VariableNode *vn = (const UA_VariableNode*)node; const UA_Variant *oldV = &vn->value.variant.value; ck_assert_ptr_eq(&oldV->type->typeId, &wValue.value.value.type->typeId); ck_assert_int_eq(20, *(UA_Int32* )resp.value.data); }END_TEST
static UA_StatusCode Service_Write_writeNode(UA_Server *server, UA_WriteValue *writeValue) { UA_StatusCode retval = UA_STATUSCODE_GOOD; const UA_Node *node; retval = UA_NodeStore_get(server->nodestore, &writeValue->nodeId, &node); if(retval) return retval; switch(writeValue->attributeId) { case UA_ATTRIBUTEID_NODEID: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_NODECLASS: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_BROWSENAME: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_DISPLAYNAME: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_DESCRIPTION: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_WRITEMASK: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_USERWRITEMASK: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_ISABSTRACT: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_SYMMETRIC: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_INVERSENAME: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_EVENTNOTIFIER: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_VALUE: if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT) { retval |= UA_Variant_copy(&writeValue->value.value, &((UA_VariableNode *)node)->value); // todo: zero-copy } break; case UA_ATTRIBUTEID_DATATYPE: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_VALUERANK: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_ARRAYDIMENSIONS: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_ACCESSLEVEL: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_USERACCESSLEVEL: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_HISTORIZING: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_EXECUTABLE: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_USEREXECUTABLE: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); return retval; }
void Service_Call_single(UA_Server *server, UA_Session *session, const UA_CallMethodRequest *request, UA_CallMethodResult *result) { /* Verify method/object relations. Object must have a hasComponent reference to the method node. */ UA_Boolean found = false; UA_NodeId hasComponentNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASCOMPONENT); result->statusCode = isNodeInTree(server->nodestore, &request->methodId, &request->objectId, &hasComponentNodeId, 1, 1, &found); if(!found) result->statusCode = UA_STATUSCODE_BADMETHODINVALID; if(result->statusCode != UA_STATUSCODE_GOOD) return; /* Get/verify the method node */ const UA_MethodNode *methodCalled = (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId); if(!methodCalled) { result->statusCode = UA_STATUSCODE_BADMETHODINVALID; return; } if(methodCalled->nodeClass != UA_NODECLASS_METHOD) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; return; } if(!methodCalled->executable || !methodCalled->userExecutable || !methodCalled->attachedMethod) { result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? return; } /* Get/verify the object node */ const UA_ObjectNode *withObject = (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, &request->objectId); if(!withObject) { result->statusCode = UA_STATUSCODE_BADNODEIDINVALID; return; } if(withObject->nodeClass != UA_NODECLASS_OBJECT && withObject->nodeClass != UA_NODECLASS_OBJECTTYPE) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; return; } /* Verify Input Argument count, types and sizes */ const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments")); if(!inputArguments) { result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT; return; } result->statusCode = argConformsToDefinition(server, inputArguments, request->inputArgumentsSize, request->inputArguments); if(result->statusCode != UA_STATUSCODE_GOOD) return; /* Allocate the output arguments */ const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments")); if(!outputArguments) { result->statusCode = UA_STATUSCODE_BADINTERNALERROR; return; } result->outputArguments = UA_Array_new(outputArguments->value.variant.value.arrayLength, &UA_TYPES[UA_TYPES_VARIANT]); if(!result->outputArguments) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength; /* Call the method */ result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, withObject->nodeId, request->inputArgumentsSize, request->inputArguments, result->outputArgumentsSize, result->outputArguments); /* TODO: Verify Output Argument count, types and sizes */ }
/** Reads a single attribute from a node in the nodestore. */ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps, const UA_ReadValueId *id, UA_DataValue *v) { if(id->dataEncoding.name.length >= 0){ if(memcmp(id->dataEncoding.name.data, "DefaultBinary", 13) != 0 && memcmp(id->dataEncoding.name.data, "DefaultXml", 10) != 0) { v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADDATAENCODINGINVALID; return; } } //index range for a non-value if(id->indexRange.length >= 0 && id->attributeId != UA_ATTRIBUTEID_VALUE){ v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADINDEXRANGENODATA; return; } UA_Node const *node = UA_NodeStore_get(server->nodestore, &(id->nodeId)); if(!node) { v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(id->attributeId) { case UA_ATTRIBUTEID_NODEID: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]); break; case UA_ATTRIBUTEID_NODECLASS: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]); break; case UA_ATTRIBUTEID_BROWSENAME: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]); break; case UA_ATTRIBUTEID_DISPLAYNAME: retval |= UA_Variant_setScalarCopy(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); if(retval == UA_STATUSCODE_GOOD) v->hasValue = UA_TRUE; break; case UA_ATTRIBUTEID_DESCRIPTION: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_WRITEMASK: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_USERWRITEMASK: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_ISABSTRACT: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->isAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_SYMMETRIC: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->symmetric, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_INVERSENAME: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->inverseName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS(UA_NODECLASS_VIEW); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode *)node)->containsNoLoops, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EVENTNOTIFIER: CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT); v->hasValue = UA_TRUE; if(node->nodeClass == UA_NODECLASS_VIEW){ retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode *)node)->eventNotifier, &UA_TYPES[UA_TYPES_BYTE]); } else { retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ObjectNode *)node)->eventNotifier, &UA_TYPES[UA_TYPES_BYTE]); } break; case UA_ATTRIBUTEID_VALUE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); { if(node->nodeClass != UA_NODECLASS_VARIABLE) { v->hasValue = UA_FALSE; handleSourceTimestamps(timestamps, v); } UA_Boolean hasRange = UA_FALSE; UA_NumericRange range; if(id->indexRange.length > 0) { retval = parse_numericrange(id->indexRange, &range); if(retval != UA_STATUSCODE_GOOD) break; hasRange = UA_TRUE; } const UA_VariableNode *vn = (const UA_VariableNode*)node; if(vn->valueSource == UA_VALUESOURCE_VARIANT) { if(hasRange) retval |= UA_Variant_copyRange(&vn->value.variant, &v->value, range); else retval |= UA_Variant_copy(&vn->value.variant, &v->value); if(retval == UA_STATUSCODE_GOOD) { v->hasValue = UA_TRUE; handleSourceTimestamps(timestamps, v); } } else { UA_DataValue val; UA_DataValue_init(&val); UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH); retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, &val); if(retval == UA_STATUSCODE_GOOD) { retval |= UA_DataValue_copy(&val, v); // todo: selection of indexranges } vn->value.dataSource.release(vn->value.dataSource.handle, &val); } if(hasRange) UA_free(range.dimensions); } break; case UA_ATTRIBUTEID_DATATYPE: { CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); const UA_VariableNode *vn = (const UA_VariableNode*)node; if(vn->valueSource == UA_VALUESOURCE_VARIANT) retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.type->typeId, &UA_TYPES[UA_TYPES_NODEID]); else { UA_DataValue val; UA_DataValue_init(&val); retval = vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val); if(retval != UA_STATUSCODE_GOOD) break; retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]); vn->value.dataSource.release(vn->value.dataSource.handle, &val); } if(retval == UA_STATUSCODE_GOOD) v->hasValue = UA_TRUE; } break; case UA_ATTRIBUTEID_VALUERANK: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode *)node)->valueRank, &UA_TYPES[UA_TYPES_INT32]); break; case UA_ATTRIBUTEID_ARRAYDIMENSIONS: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); { const UA_VariableNode *vn = (const UA_VariableNode *)node; if(vn->valueSource == UA_VALUESOURCE_VARIANT) { retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.arrayDimensions, vn->value.variant.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); if(retval == UA_STATUSCODE_GOOD) v->hasValue = UA_TRUE; } else { UA_DataValue val; UA_DataValue_init(&val); retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val); if(retval != UA_STATUSCODE_GOOD) break; if(!val.hasValue) retval = UA_STATUSCODE_BADNOTREADABLE; else retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions, val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); vn->value.dataSource.release(vn->value.dataSource.handle, &val); } } break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->accessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_USERACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->minimumSamplingInterval, &UA_TYPES[UA_TYPES_DOUBLE]); break; case UA_ATTRIBUTEID_HISTORIZING: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->historizing, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->executable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_USEREXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; default: v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); if(retval != UA_STATUSCODE_GOOD) { v->hasStatus = UA_TRUE; v->status = retval; } handleServerTimestamps(timestamps, v); }
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_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; }
static void callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequest *request, UA_CallMethodResult *result) { const UA_MethodNode *methodCalled = (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId); if(!methodCalled) { result->statusCode = UA_STATUSCODE_BADMETHODINVALID; return; } const UA_ObjectNode *withObject = (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, &request->objectId); if(!withObject) { result->statusCode = UA_STATUSCODE_BADNODEIDINVALID; goto releaseMethodReturn; } if(methodCalled->nodeClass != UA_NODECLASS_METHOD) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; goto releaseBothReturn; } if(withObject->nodeClass != UA_NODECLASS_OBJECT && withObject->nodeClass != UA_NODECLASS_OBJECTTYPE) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; goto releaseBothReturn; } /* Verify method/object relations */ // Object must have a hasComponent reference (or any inherited referenceType from sayd reference) // to be valid for a methodCall... result->statusCode = UA_STATUSCODE_BADMETHODINVALID; for(UA_Int32 i = 0; i < withObject->referencesSize; i++) { if(withObject->references[i].referenceTypeId.identifier.numeric == UA_NS0ID_HASCOMPONENT) { // FIXME: Not checking any subtypes of HasComponent at the moment if(UA_NodeId_equal(&withObject->references[i].targetId.nodeId, &methodCalled->nodeId)) { result->statusCode = UA_STATUSCODE_GOOD; break; } } } if(result->statusCode != UA_STATUSCODE_GOOD) goto releaseBothReturn; /* Verify method executable */ if(methodCalled->executable == UA_FALSE || methodCalled->userExecutable == UA_FALSE) { result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? goto releaseBothReturn; } /* Verify Input Argument count, types and sizes */ const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments")); if(inputArguments) { // Expects arguments result->statusCode = argConformsToDefinition(request, inputArguments); UA_NodeStore_release((const UA_Node*)inputArguments); if(result->statusCode != UA_STATUSCODE_GOOD) goto releaseBothReturn; } else if(request->inputArgumentsSize > 0) { // Expects no arguments, but got some result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT; goto releaseBothReturn; } const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments")); if(!outputArguments) { // A MethodNode must have an OutputArguments variable (which may be empty) result->statusCode = UA_STATUSCODE_BADINTERNALERROR; goto releaseBothReturn; } // Call method if available if(methodCalled->attachedMethod) { result->outputArguments = UA_Array_new(&UA_TYPES[UA_TYPES_VARIANT], outputArguments->value.variant.value.arrayLength); result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength; result->statusCode = methodCalled->attachedMethod(withObject->nodeId, request->inputArguments, result->outputArguments, methodCalled->methodHandle); } else result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? /* FIXME: Verify Output Argument count, types and sizes */ if(outputArguments) UA_NodeStore_release((const UA_Node*)outputArguments); releaseBothReturn: UA_NodeStore_release((const UA_Node*)withObject); releaseMethodReturn: UA_NodeStore_release((const UA_Node*)methodCalled); }
/** Reads a single attribute from a node in the nodestore. */ void Service_Read_single(UA_Server *server, UA_Session *session, const UA_TimestampsToReturn timestamps, const UA_ReadValueId *id, UA_DataValue *v) { if(id->dataEncoding.name.length > 0 && !UA_String_equal(&binEncoding, &id->dataEncoding.name)) { v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADDATAENCODINGINVALID; return; } //index range for a non-value if(id->indexRange.length > 0 && id->attributeId != UA_ATTRIBUTEID_VALUE){ v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADINDEXRANGENODATA; return; } UA_Node const *node = UA_NodeStore_get(server->nodestore, &id->nodeId); if(!node) { v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } /* When setting the value fails in the switch, we get an error code and set hasValue to false */ UA_StatusCode retval = UA_STATUSCODE_GOOD; v->hasValue = UA_TRUE; switch(id->attributeId) { case UA_ATTRIBUTEID_NODEID: forceVariantSetScalar(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]); break; case UA_ATTRIBUTEID_NODECLASS: forceVariantSetScalar(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]); break; case UA_ATTRIBUTEID_BROWSENAME: forceVariantSetScalar(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]); break; case UA_ATTRIBUTEID_DISPLAYNAME: forceVariantSetScalar(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_DESCRIPTION: forceVariantSetScalar(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_WRITEMASK: forceVariantSetScalar(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_USERWRITEMASK: forceVariantSetScalar(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_ISABSTRACT: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE); forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->isAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_SYMMETRIC: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_INVERSENAME: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS(UA_NODECLASS_VIEW); forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->containsNoLoops, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EVENTNOTIFIER: CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT); forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->eventNotifier, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_VALUE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); retval = getVariableNodeValue((const UA_VariableNode*)node, timestamps, id, v); break; case UA_ATTRIBUTEID_DATATYPE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); retval = getVariableNodeDataType((const UA_VariableNode*)node, v); break; case UA_ATTRIBUTEID_VALUERANK: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); forceVariantSetScalar(&v->value, &((const UA_VariableTypeNode*)node)->valueRank, &UA_TYPES[UA_TYPES_INT32]); break; case UA_ATTRIBUTEID_ARRAYDIMENSIONS: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); retval = getVariableNodeArrayDimensions((const UA_VariableNode*)node, v); break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->accessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_USERACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval, &UA_TYPES[UA_TYPES_DOUBLE]); break; case UA_ATTRIBUTEID_HISTORIZING: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->historizing, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); forceVariantSetScalar(&v->value, &((const UA_MethodNode*)node)->executable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_USEREXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); forceVariantSetScalar(&v->value, &((const UA_MethodNode*)node)->userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } if(retval != UA_STATUSCODE_GOOD) { v->hasValue = UA_FALSE; v->hasStatus = UA_TRUE; v->status = retval; } // Todo: what if the timestamp from the datasource are already present? handleServerTimestamps(timestamps, v); }
static UA_DataValue service_read_node(UA_Server *server, const UA_ReadValueId *id) { UA_DataValue v; UA_DataValue_init(&v); UA_Node const *node = UA_NULL; UA_Int32 result = UA_NodeStore_get(server->nodestore, &(id->nodeId), &node); if(result != UA_STATUSCODE_GOOD || node == UA_NULL) { v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; v.status = UA_STATUSCODE_BADNODEIDUNKNOWN; return v; } UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(id->attributeId) { case UA_ATTRIBUTEID_NODEID: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID], &node->nodeId); break; case UA_ATTRIBUTEID_NODECLASS: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32], &node->nodeClass); break; case UA_ATTRIBUTEID_BROWSENAME: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_QUALIFIEDNAME], &node->browseName); break; case UA_ATTRIBUTEID_DISPLAYNAME: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->displayName); break; case UA_ATTRIBUTEID_DESCRIPTION: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->description); break; case UA_ATTRIBUTEID_WRITEMASK: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32], &node->writeMask); break; case UA_ATTRIBUTEID_USERWRITEMASK: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32], &node->userWriteMask); break; case UA_ATTRIBUTEID_ISABSTRACT: CHECK_NODECLASS( UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_ReferenceTypeNode *)node)->isAbstract); break; case UA_ATTRIBUTEID_SYMMETRIC: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_ReferenceTypeNode *)node)->symmetric); break; case UA_ATTRIBUTEID_INVERSENAME: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT], &((UA_ReferenceTypeNode *)node)->inverseName); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS(UA_NODECLASS_VIEW); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_ViewNode *)node)->containsNoLoops); break; case UA_ATTRIBUTEID_EVENTNOTIFIER: CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE], &((UA_ViewNode *)node)->eventNotifier); break; case UA_ATTRIBUTEID_VALUE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copy(&((UA_VariableNode *)node)->value, &v.value); // todo: zero-copy break; case UA_ATTRIBUTEID_DATATYPE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID], &((UA_VariableTypeNode *)node)->dataType); break; case UA_ATTRIBUTEID_VALUERANK: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32], &((UA_VariableTypeNode *)node)->valueRank); break; case UA_ATTRIBUTEID_ARRAYDIMENSIONS: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; UA_Variant_copySetArray(&v.value, &UA_TYPES[UA_UINT32], ((UA_VariableTypeNode *)node)->arrayDimensionsSize, &((UA_VariableTypeNode *)node)->arrayDimensions); break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE], &((UA_VariableNode *)node)->accessLevel); break; case UA_ATTRIBUTEID_USERACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE], &((UA_VariableNode *)node)->userAccessLevel); break; case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_DOUBLE], &((UA_VariableNode *)node)->minimumSamplingInterval); break; case UA_ATTRIBUTEID_HISTORIZING: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_VariableNode *)node)->historizing); break; case UA_ATTRIBUTEID_EXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_MethodNode *)node)->executable); break; case UA_ATTRIBUTEID_USEREXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_MethodNode *)node)->userExecutable); break; default: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; v.status = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); if(retval != UA_STATUSCODE_GOOD) { v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; v.status = UA_STATUSCODE_BADNOTREADABLE; } return v; }
void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request, UA_CallResponse *response) { if(request->methodsToCallSize <= 0) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; return; } response->results = UA_Array_new(&UA_TYPES[UA_TYPES_CALLMETHODRESULT], request->methodsToCallSize); if(!response->results) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->resultsSize = request->methodsToCallSize; for(UA_Int32 i = 0; i < request->methodsToCallSize;i++) { UA_CallMethodRequest *rq = &request->methodsToCall[i]; UA_CallMethodResult *rs = &response->results[i]; /* Get/Check Nodes */ const UA_MethodNode *methodCalled = (const UA_MethodNode*) UA_NodeStore_get(server->nodestore, &rq->methodId); if(methodCalled == UA_NULL) { rs->statusCode = UA_STATUSCODE_BADMETHODINVALID; continue; } const UA_ObjectNode *withObject = (const UA_ObjectNode *) UA_NodeStore_get(server->nodestore, &rq->objectId); if(withObject == UA_NULL) { rs->statusCode = UA_STATUSCODE_BADNODEIDINVALID; printf("Obj not found\n"); continue; } if(methodCalled->nodeClass != UA_NODECLASS_METHOD) { rs->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; continue; } if(withObject->nodeClass != UA_NODECLASS_OBJECT && withObject->nodeClass != UA_NODECLASS_OBJECTTYPE) { rs->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; printf("Obj not found 1\n"); continue; } /* Verify method/object relations */ // Object must have a hasComponent reference (or any inherited referenceType from sayd reference) // to be valid for a methodCall... for(UA_Int32 i = 0; i < withObject->referencesSize; i++) { if(withObject->references[i].referenceTypeId.identifier.numeric == UA_NS0ID_HASCOMPONENT) { // FIXME: Not checking any subtypes of HasComponent at the moment if(UA_NodeId_equal(&withObject->references[i].targetId.nodeId, &methodCalled->nodeId)) { rs->statusCode = UA_STATUSCODE_GOOD; break; } } } if(rs->statusCode != UA_STATUSCODE_GOOD) continue; /* Verify method executable */ if(((const UA_MethodNode *) methodCalled)->executable == UA_FALSE || ((const UA_MethodNode *) methodCalled)->userExecutable == UA_FALSE ) { rs->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? continue; } /* Verify Input Argument count, types and sizes */ const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments")); if(inputArguments) { // Expects arguments rs->statusCode = argConformsToDefinition(rq, inputArguments); UA_NodeStore_release((const UA_Node*)inputArguments); if(rs->statusCode != UA_STATUSCODE_GOOD) continue; } else if(rq->inputArgumentsSize > 0) { // Expects no arguments, but got some rs->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT; UA_NodeStore_release((const UA_Node*)inputArguments); continue; } const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments")); if(!outputArguments) { // A MethodNode must have an OutputArguments variable (which may be empty) rs->statusCode = UA_STATUSCODE_BADINTERNALERROR; continue; } // Call method if available if(methodCalled->attachedMethod) { rs->outputArguments = UA_Array_new(&UA_TYPES[UA_TYPES_VARIANT], outputArguments->value.variant.arrayLength); rs->outputArgumentsSize = outputArguments->value.variant.arrayLength; rs->statusCode = methodCalled->attachedMethod(withObject->nodeId, rq->inputArguments, rs->outputArguments); } else rs->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? /* FIXME: Verify Output Argument count, types and sizes */ if(outputArguments) { UA_NodeStore_release((const UA_Node*)outputArguments); } UA_NodeStore_release((const UA_Node *)withObject); UA_NodeStore_release((const UA_Node *)methodCalled); } }
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 }