瀏覽代碼

export functions to add nodes in the server

Julius Pfrommer 10 年之前
父節點
當前提交
122f22d671

+ 38 - 6
examples/opcuaServer.c

@@ -5,13 +5,13 @@
 
 #include <stdio.h>
 #include <stdlib.h> 
-#ifndef WIN32
-#include <sys/mman.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <sys/time.h>
+#ifdef WIN32
+  #include "winsock2.h"
 #else
-#include "winsock2.h"
+  #include <sys/mman.h>
+  #include <sys/wait.h>
+  #include <unistd.h>
+  #include <sys/time.h>
 #endif
 #include <sys/types.h>
 #include <fcntl.h>
@@ -20,6 +20,7 @@
 #include "ua_server.h"
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
+#include "ua_namespace_0.h"
 
 UA_Boolean running = UA_TRUE;
 
@@ -63,6 +64,37 @@ int main(int argc, char** argv) {
 	UA_Server_init(&server, &endpointUrl);
 	Logger_Stdout_init(&server.logger);
     server.serverCertificate = loadCertificate();
+
+#ifdef BENCHMARK
+    UA_UInt32 nodeCount = 500;
+    UA_VariableNode *tmpNode;
+
+    UA_ExpandedNodeId objectNodeId;
+    UA_ExpandedNodeId_init(&objectNodeId);
+    objectNodeId.nodeId.identifier.numeric = 85;
+
+    UA_NodeId hasComponentReferenceId;
+    UA_NodeId_init(&hasComponentReferenceId);
+    hasComponentReferenceId.identifier.numeric = 47;
+
+    UA_Int32 data = 42;
+    char str[15];
+    for(UA_UInt32 i = 0;i<nodeCount;i++) {
+        UA_VariableNode_new(&tmpNode);
+        sprintf(str,"%d",i);
+        UA_QualifiedName_copycstring(str,&tmpNode->browseName);
+        UA_LocalizedText_copycstring(str,&tmpNode->displayName);
+        UA_LocalizedText_copycstring("integer value", &tmpNode->description);
+        tmpNode->nodeId.identifier.numeric = 19000+i;
+        tmpNode->nodeClass = UA_NODECLASS_VARIABLE;
+        //tmpNode->valueRank = -1;
+        tmpNode->value.vt = &UA_[UA_INT32];
+        tmpNode->value.storage.data.dataPtr = &data;
+        tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
+        tmpNode->value.storage.data.arrayLength = 1;
+        UA_Server_addNode(&server, (UA_Node**)&tmpNode, &objectNodeId, &hasComponentReferenceId);
+    }
+#endif
 	
 	#define PORT 16664
 	NetworklayerTCP* nl;

+ 4 - 4
include/ua_server.h

@@ -50,10 +50,10 @@ UA_Int32 UA_LIBEXPORT UA_Server_deleteMembers(UA_Server *server);
 UA_Int32 UA_LIBEXPORT UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg);
 
 /* Services for local use */
-void Server_addNodes(UA_Server *server, const UA_AddNodesRequest *request,
-                     UA_AddNodesResponse *response);
-void Server_addReferences(UA_Server *server, const UA_AddReferencesRequest *request,
-                          UA_AddReferencesResponse *response);
+UA_AddNodesResult UA_Server_addNode(UA_Server *server, UA_Node **node, UA_ExpandedNodeId *parentNodeId,
+                                    UA_NodeId *referenceTypeId);
+void UA_Server_addReferences(UA_Server *server, const UA_AddReferencesRequest *request,
+                             UA_AddReferencesResponse *response);
 
 #ifdef __cplusplus
 } // extern "C"

+ 5 - 52
src/server/ua_server.c

@@ -519,62 +519,15 @@ void UA_Server_init(UA_Server *server, UA_String *endpointUrl) {
     state->value.storageType = UA_VARIANT_DATA_NODELETE;
     UA_NodeStore_insert(server->nodestore, (UA_Node**)&state, UA_NODESTORE_INSERT_UNIQUE);
 
-#ifdef BENCHMARK
-    UA_UInt32 nodeCount = 500;
-    UA_VariableNode *tmpNode;
-    /* UA_ExpandedNodeId *tmpNodeIds; */
-    /* UA_Array_new((void**)&tmpNodes,nodeCount,&UA_[UA_VARIABLENODE]); */
-    /* UA_Array_new((void**)&tmpNodeIds,nodeCount,&UA_[UA_EXPANDEDNODEID]); */
-
-    for(UA_UInt32 i = 0;i<nodeCount;i++) {
-        UA_VariableNode_new(&tmpNode);
-        tmpNode->nodeId.identifier.numeric = 19000+i;
-        tmpNode->nodeClass = UA_NODECLASS_VARIABLE;
-
-        UA_Int32 data = 42;
-        char str[15];
-        UA_alloc((void*)str,15);
-        sprintf(str,"%d",i);
-        UA_QualifiedName_copycstring(str,&tmpNode->browseName);
-        UA_LocalizedText_copycstring(str,&tmpNode->displayName);
-
-        UA_LocalizedText_copycstring("integer value", &tmpNode->description);
-        tmpNode->value.vt = &UA_[UA_INT32];
-        tmpNode->value.storage.data.arrayDimensionsLength = 1; // added to ensure encoding in readreponse
-        tmpNode->value.storage.data.arrayLength = 1;
-        tmpNode->value.storage.data.dataPtr = &data;//&status->state; // points into the other object.
-        tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
-
-        ADDREFERENCE(tmpNode, RefTypeId_Organizes, UA_TRUE, ObjId_ObjectsFolder);
-        UA_NodeStore_insert(server->nodestore, (UA_Node**)&tmpNode, UA_NODESTORE_INSERT_UNIQUE);
-    }
-#endif
-
-    //TODO: free(namespaceArray->value.data) later or forget it
-
-    /* UA_VariableNode* v = (UA_VariableNode*)np; */
-    /* UA_Array_new((void**)&v->value.data, 2, &UA_.types[UA_STRING]); */
-    /* v->value.vt = &UA_.types[UA_STRING]; */
-    /* v->value.arrayLength = 2; */
-    /* UA_String_copycstring("http://opcfoundation.org/UA/",&((UA_String *)((v->value).data))[0]); */
-    /* UA_String_copycstring("http://localhost:16664/open62541/",&((UA_String *)(((v)->value).data))[1]); */
-    /* v->dataType.identifierType = UA_NODEIDTYPE_FOURBYTE; */
-    /* v->dataType.identifier.numeric = UA_STRING_NS0; */
-    /* v->valueRank = 1; */
-    /* v->minimumSamplingInterval = 1.0; */
-    /* v->historizing = UA_FALSE; */
-    /* UA_NodeStore_insert(server->nodestore,np); */
-
     UA_NodeStore_releaseManagedNode((const UA_Node *)root);
 }
 
-
-void Server_addNodes(UA_Server *server, const UA_AddNodesRequest *request,
-                     UA_AddNodesResponse *response) {
-    Service_AddNodes(server, &adminSession, request, response);
+UA_AddNodesResult UA_Server_addNode(UA_Server *server, UA_Node **node, UA_ExpandedNodeId *parentNodeId,
+                                    UA_NodeId *referenceTypeId) {
+    return AddNode(server, &adminSession, node, parentNodeId, referenceTypeId);
 }
 
-void Server_addReferences(UA_Server *server, const UA_AddReferencesRequest *request,
-                          UA_AddReferencesResponse *response) {
+void UA_Server_addReference(UA_Server *server, const UA_AddReferencesRequest *request,
+                            UA_AddReferencesResponse *response) {
     Service_AddReferences(server, &adminSession, request, response);
 }

+ 2 - 1
src/server/ua_services_attribute.c

@@ -375,7 +375,8 @@ void Service_Write(UA_Server *server, UA_Session *session,
                    const UA_WriteRequest *request, UA_WriteResponse *response) {
     UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
 
-    if(UA_Array_new((void **)&response->results, response->resultsSize, &UA_[UA_STATUSCODE]) != UA_SUCCESS) {
+    if(UA_Array_new((void **)&response->results, request->nodesToWriteSize, &UA_[UA_STATUSCODE])
+       != UA_SUCCESS) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }

+ 4 - 1
src/server/ua_services_internal.h

@@ -3,6 +3,7 @@
  * internally as well (with a simplified API as no access rights are checked).
  */
 
+#include "ua_session.h"
 #include "ua_nodestore.h"
 #include "ua_types_generated.h"
 
@@ -12,4 +13,6 @@
  * @param The reference itself
  * @param The namespace where the target node is looked up for the reverse reference (this is omitted if targetns is UA_NULL)
  */
-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_AddNodesResult AddNode(UA_Server *server, UA_Session *session, UA_Node **node,
+                          UA_ExpandedNodeId *parentNodeId, UA_NodeId *referenceTypeId);

+ 168 - 46
src/server/ua_services_nodemanagement.c

@@ -6,58 +6,180 @@
 #include "ua_session.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_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);
+
     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,
                       const UA_AddNodesRequest *request, UA_AddNodesResponse *response) {
     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;
     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) {
@@ -103,15 +225,15 @@ static UA_Int32 AddSingleReference(UA_Node *node, UA_ReferenceNode *reference) {
     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_Node *targetnode;
     UA_ReferenceNode inversereference;
-    if(retval != UA_SUCCESS || targetns == UA_NULL)
+    if(retval != UA_SUCCESS || nodestore == UA_NULL)
         return retval;
 
     // 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;
 
     inversereference.referenceTypeId       = reference->referenceTypeId;

+ 1 - 3
tools/generate_builtin.py

@@ -54,8 +54,6 @@ def skipType(name):
         return True
     if "Test" in name: #skip all Test types
         return True
-    if re.search("Attributes$", name) != None:
-        return True
     if re.search("NodeId$", name) != None:
         return True
     if args.only_needed and not(name in only_needed_types):
@@ -233,7 +231,7 @@ UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)''')
     if(!p) return;''')
     for n,t in membermap.iteritems():
         if t.find("*") != -1:
-            printc('\tp->%(n)sSize = 0;')
+            printc('\tp->%(n)sSize = -1;')
             printc('\tp->%(n)s = UA_NULL;')
         else:
             printc('\t%(t)s_init(&p->%(n)s);')

+ 0 - 2
tools/generate_namespace.py

@@ -65,8 +65,6 @@ def skipType(row):
         return True
     if "Test" in row[0]:
         return True
-    if re.search("Attributes$", row[0]) != None:
-        return True
     if args.only_needed and not(row[0] in only_needed_types):
         return True
     return False