|
@@ -6,58 +6,180 @@
|
|
#include "ua_session.h"
|
|
#include "ua_session.h"
|
|
#include "ua_util.h"
|
|
#include "ua_util.h"
|
|
|
|
|
|
-#define CHECKED_ACTION(ACTION, CLEAN_UP, GOTO) do { \
|
|
|
|
- status |= ACTION; \
|
|
|
|
- if(status != UA_SUCCESS) { \
|
|
|
|
- CLEAN_UP; \
|
|
|
|
- goto GOTO; \
|
|
|
|
- } } while(0) \
|
|
|
|
-
|
|
|
|
-static UA_AddNodesResult addSingleNode(UA_Server *server, UA_AddNodesItem *item) {
|
|
|
|
|
|
+#define COPY_STANDARDATTRIBUTES do { \
|
|
|
|
+ if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DISPLAYNAME) { \
|
|
|
|
+ vnode->displayName = attr.displayName; \
|
|
|
|
+ UA_LocalizedText_init(&attr.displayName); \
|
|
|
|
+ } \
|
|
|
|
+ if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DESCRIPTION) { \
|
|
|
|
+ vnode->description = attr.description; \
|
|
|
|
+ UA_LocalizedText_init(&attr.description); \
|
|
|
|
+ } \
|
|
|
|
+ if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_WRITEMASK) \
|
|
|
|
+ vnode->writeMask = attr.writeMask; \
|
|
|
|
+ if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_USERWRITEMASK) \
|
|
|
|
+ vnode->userWriteMask = attr.userWriteMask; \
|
|
|
|
+ } while(0)
|
|
|
|
+
|
|
|
|
+static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node **new_node,
|
|
|
|
+ const UA_VTable_Entry **vt) {
|
|
|
|
+ if(attributes->typeId.identifier.numeric != 357) // VariableAttributes_Encoding_DefaultBinary,357,Object
|
|
|
|
+ return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
|
|
|
|
+
|
|
|
|
+ UA_VariableAttributes attr;
|
|
|
|
+ UA_UInt32 pos = 0;
|
|
|
|
+ // todo return more informative error codes from decodeBinary
|
|
|
|
+ if(UA_VariableAttributes_decodeBinary(&attributes->body, &pos, &attr) != UA_SUCCESS)
|
|
|
|
+ return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
|
|
|
|
+
|
|
|
|
+ UA_VariableNode *vnode;
|
|
|
|
+ if(UA_VariableNode_new(&vnode) != UA_SUCCESS) {
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ARRAYDIMENSIONS) {
|
|
|
|
+ vnode->arrayDimensionsSize = attr.arrayDimensionsSize;
|
|
|
|
+ vnode->arrayDimensions = attr.arrayDimensions;
|
|
|
|
+ attr.arrayDimensionsSize = -1;
|
|
|
|
+ attr.arrayDimensions = UA_NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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 = attr.value;
|
|
|
|
+ UA_Variant_init(&attr.value);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ UA_VariableAttributes_deleteMembers(&attr);
|
|
|
|
+
|
|
|
|
+ *new_node = (UA_Node*)vnode;
|
|
|
|
+ *vt = &UA_[UA_VARIABLENODE];
|
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+UA_Int32 AddReference(UA_NodeStore *nodestore, UA_Node *node, UA_ReferenceNode *reference);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ If adding the node succeeds, the pointer will be set to zero. If the nodeid
|
|
|
|
+ of the node is null (ns=0,i=0), a unique new nodeid will be assigned and
|
|
|
|
+ returned in the AddNodesResult.
|
|
|
|
+ */
|
|
|
|
+UA_AddNodesResult AddNode(UA_Server *server, UA_Session *session, UA_Node **node,
|
|
|
|
+ UA_ExpandedNodeId *parentNodeId, UA_NodeId *referenceTypeId) {
|
|
UA_AddNodesResult result;
|
|
UA_AddNodesResult result;
|
|
UA_AddNodesResult_init(&result);
|
|
UA_AddNodesResult_init(&result);
|
|
|
|
+
|
|
|
|
+ const UA_Node *parent;
|
|
|
|
+ if(UA_NodeStore_get(server->nodestore, &parentNodeId->nodeId, &parent) != UA_SUCCESS) {
|
|
|
|
+ result.statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID;
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
|
|
- // TODO: search for namespaceUris and not only ns-ids.
|
|
|
|
- /* UA_Boolean nodeid_isnull = UA_NodeId_isNull(&item->requestedNewNodeId.nodeId); */
|
|
|
|
|
|
+ const UA_ReferenceTypeNode *referenceType;
|
|
|
|
+ if(UA_NodeStore_get(server->nodestore, referenceTypeId, (const UA_Node**)&referenceType) != UA_SUCCESS) {
|
|
|
|
+ result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
|
|
|
|
+ goto ret;
|
|
|
|
+ }
|
|
|
|
|
|
- if(item->requestedNewNodeId.nodeId.namespaceIndex == 0) {
|
|
|
|
- result.statusCode = UA_STATUSCODE_BADNODEIDREJECTED;
|
|
|
|
- return result;
|
|
|
|
|
|
+ if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
|
|
|
|
+ result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
|
|
|
|
+ goto ret2;
|
|
}
|
|
}
|
|
|
|
|
|
- UA_Int32 status = UA_SUCCESS;
|
|
|
|
- const UA_Node *parent;
|
|
|
|
- CHECKED_ACTION(UA_NodeStore_get(server->nodestore, &item->parentNodeId.nodeId, &parent),
|
|
|
|
- result.statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID, ret);
|
|
|
|
-
|
|
|
|
- /* if(!nodeid_isnull && NodeStore_contains(ns, &item->requestedNewNodeId.nodeId)) { */
|
|
|
|
- /* result.statusCode = UA_STATUSCODE_BADNODEIDEXISTS; */
|
|
|
|
- /* goto ret; */
|
|
|
|
- /* } */
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- TODO:
|
|
|
|
-
|
|
|
|
- 1) Check for the remaining conditions
|
|
|
|
- Bad_ReferenceTypeIdInvalid See Table 166 for the description of this result code.
|
|
|
|
- Bad_ReferenceNotAllowed The reference could not be created because it violates constraints imposed by the data model.
|
|
|
|
- Bad_NodeClassInvalid See Table 166 for the description of this result code.
|
|
|
|
- Bad_BrowseNameInvalid See Table 166 for the description of this result code.
|
|
|
|
- Bad_BrowseNameDuplicated The browse name is not unique among nodes that share the same relationship with the parent.
|
|
|
|
- Bad_NodeAttributesInvalid The node Attributes are not valid for the node class.
|
|
|
|
- Bad_TypeDefinitionInvalid See Table 166 for the description of this result code.
|
|
|
|
- Bad_UserAccessDenied See Table 165 for the description of this result code
|
|
|
|
-
|
|
|
|
- 2) Parse the UA_Node from the ExtensionObject
|
|
|
|
- 3) Create a new entry in the namespace
|
|
|
|
- 4) Add the reference to the parent.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-ret:
|
|
|
|
|
|
+ if(referenceType->isAbstract == UA_TRUE) {
|
|
|
|
+ result.statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED;
|
|
|
|
+ goto ret2;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // todo: test if the referenetype is hierarchical
|
|
|
|
+
|
|
|
|
+ if(UA_NodeId_isNull(&(*node)->nodeId)) {
|
|
|
|
+ if(UA_NodeStore_insert(server->nodestore, node,
|
|
|
|
+ UA_NODESTORE_INSERT_UNIQUE | UA_NODESTORE_INSERT_GETMANAGED) != UA_SUCCESS) {
|
|
|
|
+ result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
+ goto ret2;
|
|
|
|
+ }
|
|
|
|
+ result.addedNodeId = (*node)->nodeId; // cannot fail as unique nodeids are numeric
|
|
|
|
+ } else {
|
|
|
|
+ if(UA_NodeId_copy(&(*node)->nodeId, &result.addedNodeId) != UA_SUCCESS) {
|
|
|
|
+ result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
+ goto ret2;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(UA_NodeStore_insert(server->nodestore, node, UA_NODESTORE_INSERT_GETMANAGED) != UA_SUCCESS) {
|
|
|
|
+ result.statusCode = UA_STATUSCODE_BADNODEIDEXISTS; // todo: differentiate out of memory
|
|
|
|
+ UA_NodeId_deleteMembers(&result.addedNodeId);
|
|
|
|
+ goto ret2;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ UA_ReferenceNode ref;
|
|
|
|
+ UA_ReferenceNode_init(&ref);
|
|
|
|
+ ref.referenceTypeId = referenceType->nodeId; // is numeric
|
|
|
|
+ ref.isInverse = UA_TRUE; // todo: check if they are all not inverse..
|
|
|
|
+ ref.targetId.nodeId = parent->nodeId;
|
|
|
|
+ AddReference(server->nodestore, *node, &ref);
|
|
|
|
+
|
|
|
|
+ // todo: error handling. remove new node from nodestore
|
|
|
|
+
|
|
|
|
+ UA_NodeStore_releaseManagedNode(*node);
|
|
|
|
+ *node = UA_NULL;
|
|
|
|
+
|
|
|
|
+ ret2:
|
|
|
|
+ UA_NodeStore_releaseManagedNode((UA_Node*)referenceType);
|
|
|
|
+ ret:
|
|
UA_NodeStore_releaseManagedNode(parent);
|
|
UA_NodeStore_releaseManagedNode(parent);
|
|
|
|
+
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void addNodeFromAttributes(UA_Server *server, UA_Session *session, UA_AddNodesItem *item,
|
|
|
|
+ UA_AddNodesResult *result) {
|
|
|
|
+ if(item->requestedNewNodeId.nodeId.namespaceIndex == 0) {
|
|
|
|
+ // adding nodes to ns0 is not allowed over the wire
|
|
|
|
+ result->statusCode = UA_STATUSCODE_BADNODEIDREJECTED;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ UA_Node *newNode;
|
|
|
|
+ const UA_VTable_Entry *newNodeVT;
|
|
|
|
+ if(item->nodeClass == UA_NODECLASS_VARIABLE)
|
|
|
|
+ result->statusCode = parseVariableNode(&item->nodeAttributes, &newNode, &newNodeVT);
|
|
|
|
+ else // add more node types here..
|
|
|
|
+ result->statusCode = UA_STATUSCODE_BADNOTIMPLEMENTED;
|
|
|
|
+
|
|
|
|
+ if(result->statusCode != UA_STATUSCODE_GOOD)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ *result = AddNode(server, session, &newNode, &item->parentNodeId, &item->referenceTypeId);
|
|
|
|
+ if(result->statusCode != UA_STATUSCODE_GOOD)
|
|
|
|
+ newNodeVT->delete(newNode);
|
|
|
|
+}
|
|
|
|
+
|
|
void Service_AddNodes(UA_Server *server, UA_Session *session,
|
|
void Service_AddNodes(UA_Server *server, UA_Session *session,
|
|
const UA_AddNodesRequest *request, UA_AddNodesResponse *response) {
|
|
const UA_AddNodesRequest *request, UA_AddNodesResponse *response) {
|
|
UA_assert(server != UA_NULL && session != UA_NULL);
|
|
UA_assert(server != UA_NULL && session != UA_NULL);
|
|
@@ -75,7 +197,7 @@ void Service_AddNodes(UA_Server *server, UA_Session *session,
|
|
|
|
|
|
response->resultsSize = request->nodesToAddSize;
|
|
response->resultsSize = request->nodesToAddSize;
|
|
for(int i = 0;i < request->nodesToAddSize;i++)
|
|
for(int i = 0;i < request->nodesToAddSize;i++)
|
|
- response->results[i] = addSingleNode(server, &request->nodesToAdd[i]);
|
|
|
|
|
|
+ addNodeFromAttributes(server, session, &request->nodesToAdd[i], &response->results[i]);
|
|
}
|
|
}
|
|
|
|
|
|
static UA_Int32 AddSingleReference(UA_Node *node, UA_ReferenceNode *reference) {
|
|
static UA_Int32 AddSingleReference(UA_Node *node, UA_ReferenceNode *reference) {
|
|
@@ -103,15 +225,15 @@ static UA_Int32 AddSingleReference(UA_Node *node, UA_ReferenceNode *reference) {
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
-UA_Int32 AddReference(UA_NodeStore *targetns, UA_Node *node, UA_ReferenceNode *reference) {
|
|
|
|
|
|
+UA_Int32 AddReference(UA_NodeStore *nodestore, UA_Node *node, UA_ReferenceNode *reference) {
|
|
UA_Int32 retval = AddSingleReference(node, reference);
|
|
UA_Int32 retval = AddSingleReference(node, reference);
|
|
UA_Node *targetnode;
|
|
UA_Node *targetnode;
|
|
UA_ReferenceNode inversereference;
|
|
UA_ReferenceNode inversereference;
|
|
- if(retval != UA_SUCCESS || targetns == UA_NULL)
|
|
|
|
|
|
+ if(retval != UA_SUCCESS || nodestore == UA_NULL)
|
|
return retval;
|
|
return retval;
|
|
|
|
|
|
// Do a copy every time?
|
|
// Do a copy every time?
|
|
- if(UA_NodeStore_get(targetns, &reference->targetId.nodeId, (const UA_Node **)&targetnode) != UA_SUCCESS)
|
|
|
|
|
|
+ if(UA_NodeStore_get(nodestore, &reference->targetId.nodeId, (const UA_Node **)&targetnode) != UA_SUCCESS)
|
|
return UA_ERROR;
|
|
return UA_ERROR;
|
|
|
|
|
|
inversereference.referenceTypeId = reference->referenceTypeId;
|
|
inversereference.referenceTypeId = reference->referenceTypeId;
|