Przeglądaj źródła

Merge branch 'master' of https://github.com/acplt/open62541

Conflicts:
	examples/server.c
FlorianPalm 9 lat temu
rodzic
commit
fb0bee87a1

+ 7 - 0
CMakeLists.txt

@@ -159,6 +159,13 @@ endif()
 ## logging
 set(UA_LOGLEVEL 300 CACHE STRING "Level at which logs shall be reported")
 
+# Enable Methodcall service
+option(ENABLE_METHODCALLS "Enable CallMethod/MethodCall service set" OFF)
+if(ENABLE_METHODCALLS)
+    add_definitions(-DENABLE_METHODCALLS)
+    list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/src/server/ua_services_call.c)
+endif()
+
 ## multithreading
 option(ENABLE_MULTITHREADING "Enable multithreading (experimental)" OFF)
 if(ENABLE_MULTITHREADING)

+ 23 - 1
examples/client.c

@@ -100,6 +100,29 @@ int main(int argc, char *argv[]) {
     UA_WriteRequest_deleteMembers(&wReq);
     UA_WriteResponse_deleteMembers(&wResp);
 
+#ifdef ENABLE_METHODCALLS
+    /* Note:  This example requires Namespace 0 Node 11489 (ServerType -> GetMonitoredItems) 
+       FIXME: Provide a namespace 0 independant example on the server side
+     */
+    UA_Variant input;
+    
+    UA_String argString = UA_STRING("Hello Server");
+    UA_Variant_init(&input);
+    UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]);
+    
+    UA_Int32 outputSize;
+    UA_Variant *output;
+    
+    retval = UA_Client_CallServerMethod(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output);
+    if(retval == UA_STATUSCODE_GOOD) {
+        printf("Method call was successfull, and %i returned values available.\n", outputSize);
+        UA_Array_delete(output, &UA_TYPES[UA_TYPES_VARIANT], outputSize);
+    } else {
+        printf("Method call was unsuccessfull, and %x returned values available.\n", retval);
+    }
+    UA_Variant_deleteMembers(&input);
+
+#endif
 #ifdef ENABLE_ADDNODES 
     /* Create a new object type node */
     // New ReferenceType
@@ -176,7 +199,6 @@ int main(int argc, char *argv[]) {
     free(theValue);
     /* Done creating a new node*/
 #endif
-
     UA_Client_disconnect(client);
     UA_Client_delete(client);
     return UA_STATUSCODE_GOOD;

+ 33 - 0
examples/server.c

@@ -145,6 +145,15 @@ static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const
 	return UA_STATUSCODE_GOOD;
 }
 
+#ifdef ENABLE_METHODCALLS
+static UA_StatusCode getMonitoredItems(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output) {
+    UA_String tmp = UA_STRING("Hello World");
+    UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
+    printf("getMonitoredItems was called\n");
+    return UA_STATUSCODE_GOOD;
+} 
+#endif
+
 static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C\n");
 	running = 0;
@@ -273,6 +282,7 @@ int main(int argc, char** argv) {
                                   UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
    }
 
+
    //add a multidimensional Int32 array node for testing purpose
    void *value = UA_new(&UA_TYPES[UA_TYPES_INT32]);
    UA_Variant *variant = UA_Variant_new();
@@ -294,6 +304,29 @@ int main(int argc, char** argv) {
                              UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 
 
+#ifdef ENABLE_METHODCALLS
+   UA_Argument inputArguments;
+   UA_Argument_init(&inputArguments);
+   inputArguments.arrayDimensionsSize = -1;
+   inputArguments.arrayDimensions = NULL;
+   inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+   inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+   inputArguments.name = UA_STRING("Input an integer");
+   inputArguments.valueRank = -1;
+
+   UA_Argument outputArguments;
+   UA_Argument_init(&outputArguments);
+   outputArguments.arrayDimensionsSize = -1;
+   outputArguments.arrayDimensions = NULL;
+   outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+   outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+   outputArguments.name = UA_STRING("Input an integer");
+   outputArguments.valueRank = -1;
+        
+   UA_Server_addMethodNode(server, UA_QUALIFIEDNAME(1,"ping"), UA_NODEID_NUMERIC(1,62541),
+                           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                           &getMonitoredItems, 1, &inputArguments, 1, &outputArguments);
+#endif
 
 	//start server
 	UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false

+ 8 - 1
include/ua_client.h

@@ -10,7 +10,7 @@ extern "C" {
 #include "ua_connection.h"
 #include "ua_log.h"
 #include "ua_types_generated.h"
-
+    
 struct UA_Client;
 typedef struct UA_Client UA_Client;
 
@@ -60,6 +60,13 @@ UA_DeleteReferencesResponse UA_EXPORT
 
 
 /* Client-Side Macro/Procy functions */
+#ifdef ENABLE_METHODCALLS
+UA_CallResponse UA_EXPORT UA_Client_call(UA_Client *client, UA_CallRequest *request);
+UA_StatusCode UA_EXPORT UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNodeId, UA_NodeId methodNodeId,
+                                                   UA_Int32 inputSize, const UA_Variant *input,
+                                                   UA_Int32 *outputSize, UA_Variant **output);
+#endif
+    
 UA_AddNodesResponse UA_EXPORT *UA_Client_createObjectNode(  UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
                                                             UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
                                                             UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition);

+ 16 - 2
include/ua_server.h

@@ -139,8 +139,22 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
                                     const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId);
 
 UA_StatusCode UA_EXPORT
-UA_Server_AddMonodirectionalReference(UA_Server *server, UA_NodeId sourceNodeId, UA_ExpandedNodeId targetNodeId, 
-                                      UA_NodeId referenceTypeId, UA_Boolean isforward);
+UA_Server_AddMonodirectionalReference(UA_Server *server, UA_NodeId sourceNodeId,
+                                      UA_ExpandedNodeId targetNodeId, UA_NodeId referenceTypeId,
+                                      UA_Boolean isforward);
+
+
+#ifdef ENABLE_METHODCALLS
+typedef UA_StatusCode (*UA_MethodCallback)(const UA_NodeId objectId, const UA_Variant *input,
+                                           UA_Variant *output);
+    
+UA_StatusCode UA_EXPORT
+UA_Server_addMethodNode(UA_Server *server, const UA_QualifiedName browseName, UA_NodeId nodeId,
+                        const UA_ExpandedNodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        UA_MethodCallback method, UA_Int32 inputArgumentsSize,
+                        const UA_Argument *inputArguments, UA_Int32 outputArgumentsSize,
+                        const UA_Argument *outputArguments);
+#endif
 
 /** Jobs describe work that is executed once or repeatedly. */
 typedef struct {

+ 45 - 0
src/client/ua_client.c

@@ -579,9 +579,54 @@ UA_DeleteReferencesResponse UA_Client_deleteReferences(UA_Client *client, UA_Del
     return response;
 }
 
+
 /**********************************/
 /* User-Facing Macros-Function    */
 /**********************************/
+#ifdef ENABLE_METHODCALLS
+UA_CallResponse UA_Client_call(UA_Client *client, UA_CallRequest *request) {
+    UA_CallResponse response;
+    synchronousRequest(client, request, &UA_TYPES[UA_TYPES_CALLREQUEST],
+                       &response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
+    return response;
+}
+
+UA_StatusCode UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNodeId, UA_NodeId methodNodeId,
+                                         UA_Int32 inputSize, const UA_Variant *input,
+                                         UA_Int32 *outputSize, UA_Variant **output) {
+    UA_CallRequest request;
+    UA_CallRequest_init(&request);
+    
+    request.methodsToCallSize = 1;
+    request.methodsToCall = UA_CallMethodRequest_new();
+    if(!request.methodsToCall)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    UA_CallMethodRequest *rq = &request.methodsToCall[0];
+    UA_NodeId_copy(&methodNodeId, &rq->methodId);
+    UA_NodeId_copy(&objectNodeId, &rq->objectId);
+    rq->inputArguments = (void*)(uintptr_t)input; // cast const...
+    rq->inputArgumentsSize = inputSize;
+    
+    UA_CallResponse response;
+    response = UA_Client_call(client, &request);
+    rq->inputArguments = UA_NULL;
+    rq->inputArgumentsSize = -1;
+    UA_CallRequest_deleteMembers(&request);
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    retval |= response.results[0].statusCode;
+
+    if(retval == UA_STATUSCODE_GOOD) {
+        *output = response.results[0].outputArguments;
+        *outputSize = response.results[0].outputArgumentsSize;
+        response.results[0].outputArguments = UA_NULL;
+        response.results[0].outputArgumentsSize = -1;
+    }
+    UA_CallResponse_deleteMembers(&response);
+    return retval;
+}
+#endif
+
 #define ADDNODES_COPYDEFAULTATTRIBUTES(REQUEST,ATTRIBUTES) do {                           \
     ATTRIBUTES.specifiedAttributes = 0;                                                   \
     if(! UA_LocalizedText_copy(&description, &(ATTRIBUTES.description)))                  \

+ 19 - 4
src/server/ua_nodes.c

@@ -219,7 +219,7 @@ void UA_ReferenceTypeNode_delete(UA_ReferenceTypeNode *p) {
 }
 
 UA_StatusCode UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_ReferenceTypeNode *dst) {
-	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
+    UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
     if(retval)
         return retval;
     retval = UA_LocalizedText_copy(&src->inverseName, &dst->inverseName);
@@ -234,10 +234,13 @@ UA_StatusCode UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_Refe
 
 /* UA_MethodNode */
 void UA_MethodNode_init(UA_MethodNode *p) {
-	UA_Node_init((UA_Node*)p);
+    UA_Node_init((UA_Node*)p);
     p->nodeClass = UA_NODECLASS_METHOD;
     p->executable = UA_FALSE;
     p->userExecutable = UA_FALSE;
+#ifdef ENABLE_METHODCALLS
+    p->attachedMethod      = UA_NULL;
+#endif
 }
 
 UA_MethodNode * UA_MethodNode_new(void) {
@@ -248,23 +251,35 @@ UA_MethodNode * UA_MethodNode_new(void) {
 }
 
 void UA_MethodNode_deleteMembers(UA_MethodNode *p) {
+#ifdef ENABLE_METHODCALLS
+    p->attachedMethod = UA_NULL;
+#endif
     UA_Node_deleteMembers((UA_Node*)p);
 }
 
 void UA_MethodNode_delete(UA_MethodNode *p) {
     UA_MethodNode_deleteMembers(p);
+#ifdef ENABLE_METHODCALLS
+    p->attachedMethod = UA_NULL;
+#endif
     UA_free(p);
 }
 
 UA_StatusCode UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) {
+    UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
     dst->executable = src->executable;
     dst->userExecutable = src->userExecutable;
-	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
+#ifdef ENABLE_METHODCALLS
+    dst->attachedMethod = src->attachedMethod;
+#endif
+    return retval;
 }
 
 /* UA_ViewNode */
 void UA_ViewNode_init(UA_ViewNode *p) {
-	UA_Node_init((UA_Node*)p);
+    UA_Node_init((UA_Node*)p);
     p->nodeClass = UA_NODECLASS_VIEW;
     p->containsNoLoops = UA_FALSE;
     p->eventNotifier = 0;

+ 35 - 0
src/server/ua_nodes.h

@@ -20,12 +20,20 @@ typedef struct {
     UA_STANDARD_NODEMEMBERS
 } UA_Node;
 
+/**************/
+/* ObjectNode */
+/**************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Byte eventNotifier;
 } UA_ObjectNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectNode)
 
+/******************/
+/* ObjectTypeNode */
+/******************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean isAbstract;
@@ -37,6 +45,10 @@ typedef enum {
     UA_VALUESOURCE_DATASOURCE
 } UA_ValueSource;
 
+/****************/
+/* VariableNode */
+/****************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Int32 valueRank; /**< n >= 1: the value is an array with the specified number of dimensions.
@@ -59,6 +71,10 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_VariableNode)
 /** Make a copy but leave out the references and the variable */
 UA_StatusCode UA_VariableNode_copyWithoutRefsAndVariable(const UA_VariableNode *src, UA_VariableNode *dst);
 
+/********************/
+/* VariableTypeNode */
+/********************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Int32 valueRank;
@@ -72,6 +88,10 @@ typedef struct {
 } UA_VariableTypeNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_VariableTypeNode)
 
+/*********************/
+/* ReferenceTypeNode */
+/*********************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean isAbstract;
@@ -80,13 +100,24 @@ typedef struct {
 } UA_ReferenceTypeNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ReferenceTypeNode)
 
+/***********************/
+/* ReferenceMethodNode */
+/***********************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean executable;
     UA_Boolean userExecutable;
+#ifdef ENABLE_METHODCALLS
+    UA_MethodCallback attachedMethod;
+#endif
 } UA_MethodNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_MethodNode)
 
+/************/
+/* ViewNode */
+/************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean containsNoLoops;
@@ -94,6 +125,10 @@ typedef struct {
 } UA_ViewNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ViewNode)
 
+/****************/
+/* DataTypeNode */
+/****************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean isAbstract;

+ 1 - 0
src/server/ua_nodestore.c

@@ -271,6 +271,7 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node
                 identifier -= size;
         }
     } else {
+        UA_NodeId_deleteMembers(&tempNodeid);
         if(containsNodeId(ns, &node->nodeId, &slot))
             return UA_STATUSCODE_BADNODEIDEXISTS;
     }

+ 1 - 1
src/server/ua_server.c

@@ -466,7 +466,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 #define HUNDRED_NANOSEC_PER_USEC 10LL
 #define HUNDRED_NANOSEC_PER_SEC (HUNDRED_NANOSEC_PER_USEC * 1000000LL)
     server->buildDate = (mktime(&ct) + UNIX_EPOCH_BIAS_SEC) * HUNDRED_NANOSEC_PER_SEC;
-
+    
     /**************/
     /* References */
     /**************/

+ 70 - 0
src/server/ua_server_addressspace.c

@@ -335,6 +335,7 @@ UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *no
     //namespace index is assumed to be valid
     const UA_Node *managed = UA_NULL;
     UA_NodeId tempNodeid;
+    UA_NodeId_init(&tempNodeid);
     UA_NodeId_copy(&node->nodeId, &tempNodeid);
     tempNodeid.namespaceIndex = 0;
     if(UA_NodeId_isNull(&tempNodeid)) {
@@ -370,9 +371,78 @@ UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *no
     UA_NodeStore_release(managed);
     
  ret2:
+    UA_NodeId_deleteMembers(&tempNodeid);
     UA_NodeStore_release((const UA_Node*)referenceType);
  ret:
     UA_NodeStore_release(parent);
 
     return result;
 }
+
+#ifdef ENABLE_METHODCALLS
+UA_StatusCode
+UA_Server_addMethodNode(UA_Server *server, const UA_QualifiedName browseName, UA_NodeId nodeId,
+                        const UA_ExpandedNodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        UA_MethodCallback method, UA_Int32 inputArgumentsSize,
+                        const UA_Argument *inputArguments, UA_Int32 outputArgumentsSize,
+                        const UA_Argument *outputArguments) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    
+    UA_MethodNode *newMethod = UA_MethodNode_new();
+    UA_NodeId_copy(&nodeId, &newMethod->nodeId);
+    UA_QualifiedName_copy(&browseName, &newMethod->browseName);
+    UA_String_copy(&browseName.name, &newMethod->displayName.text);
+    
+    newMethod->attachedMethod = method;
+    newMethod->executable = UA_TRUE;
+    newMethod->userExecutable = UA_TRUE;
+
+    UA_ExpandedNodeId methodExpandedNodeId;
+    UA_ExpandedNodeId_init(&methodExpandedNodeId);
+    UA_NodeId_copy(&newMethod->nodeId, &methodExpandedNodeId.nodeId);
+    
+    UA_AddNodesResult addRes = UA_Server_addNode(server, (UA_Node *) newMethod, parentNodeId, referenceTypeId);
+    if (addRes.statusCode != UA_STATUSCODE_GOOD) {
+        UA_MethodNode_delete(newMethod);
+        UA_ExpandedNodeId_deleteMembers(&methodExpandedNodeId);
+        return addRes.statusCode;
+    }
+    
+    // Create InputArguments
+    UA_NodeId argId = UA_NODEID_NUMERIC(nodeId.namespaceIndex, 0); 
+    UA_VariableNode *inputArgumentsVariableNode  = UA_VariableNode_new();
+    retval |= UA_NodeId_copy(&argId, &inputArgumentsVariableNode->nodeId);
+    inputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0,"InputArguments");
+    inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
+    inputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
+    inputArgumentsVariableNode->valueRank = 1;
+    UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.variant, inputArguments, inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
+    
+    addRes = UA_Server_addNode(server, (UA_Node *) inputArgumentsVariableNode, methodExpandedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+
+    if (addRes.statusCode != UA_STATUSCODE_GOOD) {
+        UA_ExpandedNodeId_deleteMembers(&methodExpandedNodeId);
+        // TODO Remove node
+        return addRes.statusCode;
+    }
+    
+    // Create OutputArguments
+    argId = UA_NODEID_NUMERIC(nodeId.namespaceIndex, 0);
+    UA_VariableNode *outputArgumentsVariableNode  = UA_VariableNode_new();
+    retval |= UA_NodeId_copy(&argId, &outputArgumentsVariableNode->nodeId);
+    outputArgumentsVariableNode->browseName  = UA_QUALIFIEDNAME_ALLOC(0,"OutputArguments");
+    outputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
+    outputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
+    outputArgumentsVariableNode->valueRank = 1;
+    UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.variant, outputArguments, outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
+    // Create Arguments Variant
+    addRes = UA_Server_addNode(server, (UA_Node *) outputArgumentsVariableNode, methodExpandedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+
+    UA_ExpandedNodeId_deleteMembers(&methodExpandedNodeId);
+    if (addRes.statusCode != UA_STATUSCODE_GOOD)
+        // TODO Remove node
+        return addRes.statusCode;
+    
+    return retval;
+}
+#endif

+ 6 - 0
src/server/ua_server_binary.c

@@ -6,6 +6,7 @@
 #include "ua_statuscodes.h"
 #include "ua_securechannel_manager.h"
 #include "ua_session_manager.h"
+
 /** Max size of messages that are allocated on the stack */
 #define MAX_STACK_MESSAGE 65536
 
@@ -326,6 +327,11 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
     case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST:
         INVOKE_SERVICE(TranslateBrowsePathsToNodeIds, UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE);
         break;
+#ifdef ENABLE_METHODCALLS
+    case UA_NS0ID_CALLREQUEST:
+        INVOKE_SERVICE(Call, UA_TYPES_CALLRESPONSE);
+	break;
+#endif
 #ifdef ENABLE_ADDNODES 
     case UA_NS0ID_ADDNODESREQUEST:
         INVOKE_SERVICE(AddNodes, UA_TYPES_ADDNODESRESPONSE);

+ 1 - 1
src/server/ua_server_internal.h

@@ -50,7 +50,7 @@ struct UA_Server {
 
     /* Jobs with a repetition interval */
     LIST_HEAD(RepeatedJobsList, RepeatedJobs) repeatedJobs;
-
+    
 #ifdef UA_MULTITHREADING
     /* Dispatch queue head for the worker threads (the tail should not be in the same cache line) */
 	struct cds_wfcq_head dispatchQueue_head;

+ 16 - 0
src/server/ua_services.h

@@ -188,6 +188,11 @@ void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_Un
  * @{
  */
 
+/* Mock-Up of the function signature for Unit Tests */
+#ifdef BUILD_UNIT_TESTS
+UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range);
+#endif
+
 /**
  * Used to read one or more Attributes of one or more Nodes. For constructed
  * Attribute values whose elements are indexed, such as an array, this Service
@@ -212,6 +217,12 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
  */
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
                    UA_WriteResponse *response);
+
+/* Mock-Up of the function signature for Unit Tests */
+#ifdef BUILD_UNIT_TESTS
+UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
+#endif
+
 // Service_HistoryUpdate
 /** @} */
 
@@ -279,4 +290,9 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
 /** @} */
 /** @} */
 
+#ifdef ENABLE_METHODCALLS
+void Service_Call(UA_Server *server, UA_Session *session,
+                  const UA_CallRequest *request,
+                  UA_CallResponse *response);
+#endif
 #endif /* UA_SERVICES_H_ */

+ 8 - 2
src/server/ua_services_attribute.c

@@ -5,7 +5,10 @@
 #include "ua_nodestore.h"
 #include "ua_util.h"
 
-static UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
+#ifndef BUILD_UNIT_TESTS
+static
+#endif
+UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
     if(str.length < 0 || str.length >= 1023)
         return UA_STATUSCODE_BADINTERNALERROR;
 #ifdef NO_ALLOCA
@@ -423,7 +426,10 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 #endif
 }
 
-static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
+#ifndef BUILD_UNIT_TESTS
+static
+#endif
+UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
     /* is there a value at all */

+ 204 - 0
src/server/ua_services_call.c

@@ -0,0 +1,204 @@
+#include "ua_services.h"
+#include "ua_server_internal.h"
+#include "ua_statuscodes.h"
+#include "ua_util.h"
+#include "ua_nodestore.h"
+#include "ua_nodes.h"
+
+static const UA_VariableNode *getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
+                                                       UA_String withBrowseName) {
+    const UA_Node *refTarget;
+    UA_NodeId hasProperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
+    
+    for(UA_Int32 i = 0; i < ofMethod->referencesSize; i++) {
+        if(ofMethod->references[i].isInverse == UA_FALSE && 
+            UA_NodeId_equal(&hasProperty, &ofMethod->references[i].referenceTypeId)) {
+            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;
+            }
+            UA_NodeStore_release(refTarget);
+        }
+    }
+    return UA_NULL;
+}
+
+static UA_StatusCode statisfySignature(UA_Variant *var, UA_Argument arg) {
+    if(!UA_NodeId_equal(&var->type->typeId, &arg.dataType) )
+        return UA_STATUSCODE_BADINVALIDARGUMENT;
+    
+    // Note: The namespace compiler will compile nodes with their actual array dimensions, never -1
+    if(arg.arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0)
+        if(var->arrayDimensionsSize != arg.arrayDimensionsSize) 
+            return UA_STATUSCODE_BADINVALIDARGUMENT;
+        
+        // Continue with jpfr's statisfySignature from here on
+        /* ValueRank Semantics
+         *  n >= 1: the value is an array with the specified number of dimens*ions.
+         *  n = 0: the value is an array with one or more dimensions.
+         *  n = -1: the value is a scalar.
+         *  n = -2: the value can be a scalar or an array with any number of dimensions.
+         *  n = -3:  the value can be a scalar or a one dimensional array. */
+        UA_Boolean scalar = UA_Variant_isScalar(var);
+        if(arg.valueRank == 0 && scalar)
+            return UA_STATUSCODE_BADINVALIDARGUMENT;
+        if(arg.valueRank == -1 && !scalar)
+            return UA_STATUSCODE_BADINVALIDARGUMENT;
+        if(arg.valueRank == -3 && var->arrayDimensionsSize > 1)
+            return UA_STATUSCODE_BADINVALIDARGUMENT;
+        if(arg.valueRank >= 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize)
+            return UA_STATUSCODE_BADINVALIDARGUMENT;
+        
+        if(arg.arrayDimensionsSize >= 1) {
+            if(arg.arrayDimensionsSize != var->arrayDimensionsSize)
+                return UA_STATUSCODE_BADINVALIDARGUMENT;
+            for(UA_Int32 i = 0; i < arg.arrayDimensionsSize; i++) {
+                if(arg.arrayDimensions[i] != (UA_UInt32) var->arrayDimensions[i])
+                    return UA_STATUSCODE_BADINVALIDARGUMENT;
+            }
+        }
+   return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode argConformsToDefinition(UA_CallMethodRequest *rs, const UA_VariableNode *argDefinition) {
+    if(argDefinition->value.variant.type != &UA_TYPES[UA_TYPES_ARGUMENT] &&
+        argDefinition->value.variant.type != &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])
+        return UA_STATUSCODE_BADINTERNALERROR;
+    if(rs->inputArgumentsSize < argDefinition->value.variant.arrayLength) 
+        return UA_STATUSCODE_BADARGUMENTSMISSING;
+    if(rs->inputArgumentsSize > argDefinition->value.variant.arrayLength)
+        return UA_STATUSCODE_BADINVALIDARGUMENT;
+    
+    const UA_ExtensionObject *thisArgDefExtObj;
+    UA_Variant *var;
+    UA_Argument arg;
+    size_t decodingOffset = 0;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_NodeId ArgumentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ARGUMENT + UA_ENCODINGOFFSET_BINARY);
+    for(int i = 0; i<rs->inputArgumentsSize; i++) {
+        var = &rs->inputArguments[i];
+        if(argDefinition->value.variant.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]) {
+            thisArgDefExtObj = &((const UA_ExtensionObject *) (argDefinition->value.variant.data))[i];
+            decodingOffset = 0;
+            
+            if(!UA_NodeId_equal(&ArgumentNodeId, &thisArgDefExtObj->typeId))
+                return UA_STATUSCODE_BADINTERNALERROR;
+                
+            UA_decodeBinary(&thisArgDefExtObj->body, &decodingOffset, &arg, &UA_TYPES[UA_TYPES_ARGUMENT]);
+        } else if(argDefinition->value.variant.type == &UA_TYPES[UA_TYPES_ARGUMENT])
+            arg = ((UA_Argument *) argDefinition->value.variant.data)[i];
+        retval |= statisfySignature(var, arg);
+    }
+    return retval;
+}
+
+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) {
+            rs->statusCode = UA_STATUSCODE_BADINTERNALERROR;
+            UA_NodeStore_release((const UA_Node*)outputArguments);
+            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?
+
+        UA_NodeStore_release((const UA_Node*)outputArguments);
+        UA_NodeStore_release((const UA_Node *)withObject);
+        UA_NodeStore_release((const UA_Node *)methodCalled);
+    }
+}

+ 419 - 34
tests/check_services_attributes.c

@@ -46,16 +46,13 @@ static UA_Server* makeTestSequence(void) {
 	UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Demo"), UA_NODEID_NUMERIC(1, 50), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
 
 	/* ReferenceTypeNode */
-    UA_ReferenceTypeNode *hierarchicalreferences = UA_ReferenceTypeNode_new();
-    copyNames((UA_Node*)hierarchicalreferences, "Hierarchicalreferences");
-    hierarchicalreferences->nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
-    hierarchicalreferences->isAbstract = UA_TRUE;
-    hierarchicalreferences->symmetric  = UA_FALSE;
-    hierarchicalreferences->inverseName = UA_LOCALIZEDTEXT("", "test");
-
-    UA_Server_addNode(server, (UA_Node*)hierarchicalreferences,
-                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
-                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+	UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
+	copyNames((UA_Node*)organizes, "Organizes");
+	organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrganizedBy");
+	organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
+	organizes->isAbstract = UA_FALSE;
+	organizes->symmetric  = UA_FALSE;
+	UA_Server_addNode(server, (UA_Node*)organizes, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
 	/* ViewNode */
     UA_ViewNode *viewtest = UA_ViewNode_new();
@@ -167,7 +164,8 @@ START_TEST(ReadSingleAttributeNodeClassWithoutTimestamp)
 				&resp);
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
-		//ck_assert_int_eq(&UA_TYPES[UA_TYPES_NODECLASS],resp.value.type);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_INT32],resp.value.type);
+		ck_assert_int_eq(*(UA_Int32*)resp.value.data,UA_NODECLASS_VARIABLE);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeBrowseNameWithoutTimestamp)
@@ -283,13 +281,13 @@ START_TEST(ReadSingleAttributeWriteMaskWithoutTimestamp)
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
-		//UA_UInt32* respval;
-		//respval = (UA_UInt32*) resp.value.data;
-		//UA_VariableNode* compNode = makeCompareSequence();
+
+		UA_UInt32* respval;
+		respval = (UA_UInt32*) resp.value.data;
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
-		//ck_assert_int_eq(*(UA_UInt32* )compNode->writeMask,respval);
+		ck_assert_int_eq(0,*respval);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeUserWriteMaskWithoutTimestamp)
@@ -308,8 +306,12 @@ START_TEST(ReadSingleAttributeUserWriteMaskWithoutTimestamp)
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
 
+		UA_UInt32* respval;
+		respval = (UA_UInt32*) resp.value.data;
+
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
+		ck_assert_int_eq(0,*respval);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp)
@@ -322,7 +324,7 @@ START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp)
 		UA_ReadRequest_init(&rReq);
 		rReq.nodesToRead = UA_ReadValueId_new();
 		rReq.nodesToReadSize = 1;
-		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ISABSTRACT;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
@@ -330,7 +332,7 @@ START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp)
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
-		ck_assert(*(UA_Boolean* )resp.value.data==UA_TRUE);
+		ck_assert(*(UA_Boolean* )resp.value.data==UA_FALSE);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeSymmetricWithoutTimestamp)
@@ -343,7 +345,7 @@ START_TEST(ReadSingleAttributeSymmetricWithoutTimestamp)
 		UA_ReadRequest_init(&rReq);
 		rReq.nodesToRead = UA_ReadValueId_new();
 		rReq.nodesToReadSize = 1;
-		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_SYMMETRIC;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
@@ -364,19 +366,19 @@ START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp)
 		UA_ReadRequest_init(&rReq);
 		rReq.nodesToRead = UA_ReadValueId_new();
 		rReq.nodesToReadSize = 1;
-		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_INVERSENAME;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
 
-		/*UA_LocalizedText* respval;
+		UA_LocalizedText* respval;
 		respval = (UA_LocalizedText*) resp.value.data;
-		const UA_LocalizedText comp = UA_LOCALIZEDTEXT("", "test");
-*/
+		const UA_LocalizedText comp = UA_LOCALIZEDTEXT("", "OrganizedBy");
+
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT],resp.value.type);
-		/*ck_assert_int_eq(comp.text.length, respval->text.length);
+		ck_assert_int_eq(comp.text.length, respval->text.length);
 		for (int var = 0; var < respval->text.length - 1; ++var) {
 			ck_assert_int_eq(comp.text.data[var], respval->text.data[var]);
 		}
@@ -384,7 +386,7 @@ START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp)
 		for (int var = 0; var < respval->locale.length - 1; ++var) {
 			ck_assert_int_eq(comp.locale.data[var], respval->locale.data[var]);
 		}
-		UA_free(respval);*/
+		UA_free(respval);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeContainsNoLoopsWithoutTimestamp)
@@ -445,13 +447,13 @@ START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp)
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
 
-		//UA_NodeId* respval;
-		//respval = (UA_NodeId*) resp.value.data;
-		//const UA_VariableNode compNode = makeCompareSequence();
-		//const UA_NodeId comp = compNode;
+		UA_NodeId* respval;
+		respval = (UA_NodeId*) resp.value.data;
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
+		ck_assert_int_eq(respval->namespaceIndex,0);
+		ck_assert_int_eq(respval->identifier.numeric,UA_NS0ID_INT32);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeValueRankWithoutTimestamp)
@@ -494,6 +496,7 @@ START_TEST(ReadSingleAttributeArrayDimensionsWithoutTimestamp)
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
+		ck_assert_int_eq((UA_Int32*)resp.value.data,0);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeAccessLevelWithoutTimestamp)
@@ -556,13 +559,15 @@ START_TEST(ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp)
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
 
-		//UA_Double* respval;
-		//respval = (UA_Double*) resp.value.data;
-		//UA_VariableNode* compNode = makeCompareSequence();
+		UA_Double* respval;
+		respval = (UA_Double*) resp.value.data;
+		UA_VariableNode *compNode = makeCompareSequence();
+		UA_Double comp;
+		comp = (UA_Double) compNode->minimumSamplingInterval;
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_DOUBLE], resp.value.type);
-		//ck_assert_int_eq(compNode->minimumSamplingInterval,respval);
+		ck_assert(*respval == comp);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeHistorizingWithoutTimestamp)
@@ -596,13 +601,14 @@ START_TEST(ReadSingleAttributeExecutableWithoutTimestamp)
 		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].nodeId.identifier.numeric = UA_NS0ID_METHODNODE;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_EXECUTABLE;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+		ck_assert(*(UA_Boolean*)resp.value.data==UA_FALSE);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp)
@@ -615,7 +621,7 @@ START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp)
 		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].nodeId.identifier.numeric = UA_NS0ID_METHODNODE;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
@@ -623,6 +629,353 @@ START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp)
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+		ck_assert(*(UA_Boolean*)resp.value.data==UA_FALSE);
+	}END_TEST
+
+
+/* Tests for writeValue method */
+
+START_TEST(WriteSingleAttributeNodeId)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_NODEID;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeNodeclass)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_NODECLASS;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeBrowseName)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_BROWSENAME;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeDisplayName)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeDescription)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_DESCRIPTION;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeWriteMask)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_WRITEMASK;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeUserWriteMask)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_USERWRITEMASK;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeIsAbstract)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_ISABSTRACT;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeSymmetric)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_SYMMETRIC;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeInverseName)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_INVERSENAME;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeContainsNoLoops)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_CONTAINSNOLOOPS;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeEventNotifier)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}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;
+		ck_assert_int_eq(&oldV->type->typeId, &wValue.value.value.type->typeId);
+
+		ck_assert_int_eq(20, *(UA_Int32* )resp.value.data);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeDataType)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_DATATYPE;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeValueRank)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_VALUERANK;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeArrayDimensions)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeAccessLevel)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_ACCESSLEVEL;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeUserAccessLevel)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_USERACCESSLEVEL;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeMinimumSamplingInterval)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeHistorizing)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_HISTORIZING;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeExecutable)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_EXECUTABLE;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeUserExecutable)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeNoValue)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
+		wValue.value.hasValue = UA_FALSE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADTYPEMISMATCH);
+	}END_TEST
+
+START_TEST(numericRange)
+	{
+		//UA_Server *server = makeTestSequence();
+
+        UA_NumericRange range;
+		const UA_String str = (UA_String){9, (UA_Byte*)"1:2,0:3,5"};
+		UA_StatusCode retval = parse_numericrange(str, &range);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+		ck_assert_int_eq(range.dimensionsSize,3);
+		ck_assert_int_eq(range.dimensions[0].min,1);
+		ck_assert_int_eq(range.dimensions[0].max,2);
+		ck_assert_int_eq(range.dimensions[1].min,0);
+		ck_assert_int_eq(range.dimensions[1].max,3);
+		ck_assert_int_eq(range.dimensions[2].min,5);
+		ck_assert_int_eq(range.dimensions[2].max,5);
 	}END_TEST
 
 static Suite * testSuite_services_attributes(void) {
@@ -675,6 +1028,38 @@ static Suite * testSuite_services_attributes(void) {
 			ReadSingleAttributeUserExecutableWithoutTimestamp);
 
 	suite_add_tcase(s, tc_readSingleAttributes);
+
+	TCase *tc_writeSingleAttributes = tcase_create("writeSingleAttributes");
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNodeId);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNodeclass);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeBrowseName);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDisplayName);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDescription);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeWriteMask);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserWriteMask);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeIsAbstract);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeSymmetric);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeInverseName);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeContainsNoLoops);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeEventNotifier);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValue);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDataType);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRank);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeArrayDimensions);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeAccessLevel);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserAccessLevel);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeMinimumSamplingInterval);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeHistorizing);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeExecutable);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserExecutable);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNoValue);
+
+	suite_add_tcase(s, tc_writeSingleAttributes);
+
+	TCase *tc_parseNumericRange = tcase_create("parseNumericRange");
+	tcase_add_test(tc_parseNumericRange, numericRange);
+	suite_add_tcase(s, tc_parseNumericRange);
+
 	return s;
 }
 

+ 3 - 2
tools/generate_datatypes.py

@@ -57,8 +57,9 @@ minimal_types = ["InvalidType", "Node", "NodeClass", "ReferenceNode", "Applicati
                  "NodeAttributes","ReferenceTypeAttributes", "ViewAttributes", "ObjectTypeAttributes",
                  "NodeAttributesMask","DeleteNodesItem", "DeleteNodesRequest", "DeleteNodesResponse",
                  "DeleteReferencesItem", "DeleteReferencesRequest", "DeleteReferencesResponse",
-                 "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse",
-                 "UserIdentityToken", "UserNameIdentityToken", "AnonymousIdentityToken", "ServiceFault"]
+                 "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse", 
+                 "UserIdentityToken", "UserNameIdentityToken", "AnonymousIdentityToken", "ServiceFault",
+                 "CallMethodRequest", "CallMethodResult", "CallResponse", "CallRequest", "Argument"]
 
 class TypeDescription(object):
     def __init__(self, name, nodeid, namespaceid):

+ 2 - 2
tools/pyUANamespace/open62541_MacroHelper.py

@@ -69,12 +69,12 @@ class open62541_MacroHelper():
       #code.append(refid + ".isForward = UA_FALSE;")
     #code.append(refid + ".targetNodeId = " + self.getCreateExpandedNodeIDMacro(reference.target()) + ";")
     #code.append("addOneWayReferenceWithSession(server, (UA_Session *) UA_NULL, &" + refid + ");")
-    
+
     if reference.isForward():
       code.append("UA_Server_AddMonodirectionalReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", UA_TRUE);")
     else:
       code.append("UA_Server_AddMonodirectionalReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", UA_FALSE);")
-    
+
     return code
 
   def getCreateNode(self, node):

+ 239 - 62
tools/pyUANamespace/ua_builtin_types.py

@@ -5,8 +5,8 @@
 ### Author:  Chris Iatrou (ichrispa@core-vector.net)
 ### Version: rev 13
 ###
-### This program was created for educational purposes and has been 
-### contributed to the open62541 project by the author. All licensing 
+### This program was created for educational purposes and has been
+### contributed to the open62541 project by the author. All licensing
 ### terms for this source is inherited by the terms and conditions
 ### specified for by the open62541 project (see the projects readme
 ### file for more information on the LGPL terms and restrictions).
@@ -19,6 +19,7 @@
 import xml.dom.minidom as dom
 from ua_constants import *
 from logger import *
+from open62541_MacroHelper import open62541_MacroHelper
 
 def getNextElementNode(xmlvalue):
   if xmlvalue == None:
@@ -51,6 +52,14 @@ class opcua_value_t():
                        'string', 'bytestring', 'localizedtext', 'statuscode', \
                        'diagnosticinfo', 'nodeid', 'guid', 'datetime', \
                        'qualifiedname', 'expandednodeid', 'xmlelement']
+    self.dataType = None
+    self.encodingRule = []
+
+  def setEncodingRule(self, encoding):
+    self.encodingRule = encoding
+
+  def getEncodingRule(self):
+    return self.encodingRule
 
   def alias(self, data=None):
     if not data == None:
@@ -67,55 +76,78 @@ class opcua_value_t():
       self.__value__ = data
     return self.__value__
 
-  def getTypeByString(self, stringName):
+  def getTypeByString(self, stringName, encodingRule):
     stringName = str(stringName.lower())
     if stringName == 'boolean':
-      return opcua_BuiltinType_boolean_t(self.parent)
+      t = opcua_BuiltinType_boolean_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'int32':
-      return opcua_BuiltinType_int32_t(self.parent)
+      t = opcua_BuiltinType_int32_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'uint32':
-      return opcua_BuiltinType_uint32_t(self.parent)
+      t = opcua_BuiltinType_uint32_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'int16':
-      return opcua_BuiltinType_int16_t(self.parent)
+      t = opcua_BuiltinType_int16_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'uint16':
-      return opcua_BuiltinType_uint16_t(self.parent)
+      t = opcua_BuiltinType_uint16_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'int64':
-      return opcua_BuiltinType_int64_t(self.parent)
+      t = opcua_BuiltinType_int64_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'uint64':
-      return opcua_BuiltinType_uint64_t(self.parent)
+      t = opcua_BuiltinType_uint64_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'byte':
-      return opcua_BuiltinType_byte_t(self.parent)
+      t = opcua_BuiltinType_byte_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'sbyte':
-      return opcua_BuiltinType_sbyte_t(self.parent)
+      t = opcua_BuiltinType_sbyte_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'float':
-      return opcua_BuiltinType_float_t(self.parent)
+      t = opcua_BuiltinType_float_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'double':
-      return opcua_BuiltinType_double_t(self.parent)
+      t = opcua_BuiltinType_double_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'string':
-      return opcua_BuiltinType_string_t(self.parent)
+      t = opcua_BuiltinType_string_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'bytestring':
-      return opcua_BuiltinType_bytestring_t(self.parent)
+      t = opcua_BuiltinType_bytestring_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'localizedtext':
-      return opcua_BuiltinType_localizedtext_t(self.parent)
+      t = opcua_BuiltinType_localizedtext_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'statuscode':
-      return opcua_BuiltinType_statuscode_t(self.parent)
+      t = opcua_BuiltinType_statuscode_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'diagnosticinfo':
-      return opcua_BuiltinType_diagnosticinfo_t(self.parent)
+      t = opcua_BuiltinType_diagnosticinfo_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'nodeid':
-      return opcua_BuiltinType_nodeid_t(self.parent)
+      t = opcua_BuiltinType_nodeid_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'guid':
-      return opcua_BuiltinType_guid_t(self.parent)
+      t = opcua_BuiltinType_guid_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'datetime':
-      return opcua_BuiltinType_datetime_t(self.parent)
+      t = opcua_BuiltinType_datetime_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'qualifiedname':
-      return opcua_BuiltinType_qualifiedname_t(self.parent)
+      t = opcua_BuiltinType_qualifiedname_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'expandednodeid':
-      return opcua_BuiltinType_expandednodeid_t(self.parent)
+      t = opcua_BuiltinType_expandednodeid_t(self.parent)
+      t.setEncodingRule(encodingRule)
     elif stringName == 'xmlelement':
-      return opcua_BuiltinType_xmlelement_t(self.parent)
+      t = opcua_BuiltinType_xmlelement_t(self.parent)
+      t.setEncodingRule(encodingRule)
     else:
       log(self, "No class representing stringName " + stringName + " was found. Cannot create builtinType.")
       return None
+    return t
 
   def parseXML(self, xmlvalue):
     log(self, "parsing xmlvalue for " + self.parent.browseName() + " (" + str(self.parent.id()) + ") according to " + str(self.parent.dataType().target().getEncoding()))
@@ -163,7 +195,7 @@ class opcua_value_t():
     # such as extension Objects.
     if len(enc) == 1:
       # 0: ['BuiltinType']          either builtin type
-      # 1: [ [ 'Alias', [...]] ] or single alias for possible multipart
+      # 1: [ [ 'Alias', [...], n] ] or single alias for possible multipart
       if isinstance(enc[0], str):
         # 0: 'BuiltinType'
         if alias != None:
@@ -171,7 +203,7 @@ class opcua_value_t():
             log(self, "Expected XML element with tag " + alias + " but found " + xmlvalue.tagName + " instead", LOG_LEVEL_ERROR)
             return None
           else:
-            t = self.getTypeByString(enc[0])
+            t = self.getTypeByString(enc[0], enc)
             t.alias(alias)
             t.parseXML(xmlvalue)
             return t
@@ -179,15 +211,15 @@ class opcua_value_t():
           if not self.isBuiltinByString(xmlvalue.tagName):
             log(self, "Expected XML describing builtin type " + enc[0] + " but found " + xmlvalue.tagName + " instead", LOG_LEVEL_ERROR)
           else:
-            t = self.getTypeByString(enc[0])
+            t = self.getTypeByString(enc[0], enc)
             t.parseXML(xmlvalue)
             return t
       else:
-        # 1: ['Alias', [...]]
+        # 1: ['Alias', [...], n]
         # Let the next elif handle this
         return self.__parseXMLSingleValue(xmlvalue, alias=alias, encodingPart=enc[0])
-    elif len(enc) == 2 and isinstance(enc[0], str):
-      # [ 'Alias', [...] ]          aliased multipart
+    elif len(enc) == 3 and isinstance(enc[0], str):
+      # [ 'Alias', [...], 0 ]          aliased multipart
       if alias == None:
         alias = enc[0]
       # if we have an alias and the next field is multipart, keep the alias
@@ -209,7 +241,7 @@ class opcua_value_t():
         return None
 
       extobj = opcua_BuiltinType_extensionObject_t(self.parent)
-
+      extobj.setEncodingRule(enc)
       etype = xmlvalue.getElementsByTagName("TypeId")
       if len(etype) == 0:
         log(self, "Did not find <TypeId> for ExtensionObject", LOG_LEVEL_ERROR)
@@ -277,17 +309,26 @@ class opcua_value_t():
   def __repr__(self):
     return self.__str__()
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return ""
 
   def printOpen62541CCode(self):
+    codegen = open62541_MacroHelper()
     code = []
     valueName = self.parent.getCodePrintableID() + "_variant_DataContents"
 
     # self.value either contains a list of multiple identical BUILTINTYPES, or it
     # contains a single builtintype (which may be a container); choose if we need
     # to create an array or a single variable.
-    if len(self.value) > 1:
+    # Note that some genious defined that there are arrays of size 1, which are
+    # distinctly different then a single value, so we need to check that as well
+    # Semantics:
+    # -3: Scalar or 1-dim
+    # -2: Scalar or x-dim | x>0
+    # -1: Scalar
+    #  0: x-dim | x>0
+    #  n: n-dim | n>0
+    if self.parent.valueRank() != -1 and (self.parent.valueRank() >=0 or (len(self.value) > 1 and (self.parent.valueRank() != -2 or self.parent.valueRank() != -3))):
       # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
       if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_GUID:
         log(self, "Don't know how to print array of GUID in node " + str(self.parent.id()), LOG_LEVEL_WARN)
@@ -295,15 +336,24 @@ class opcua_value_t():
         log(self, "Don't know how to print array of DateTime in node " + str(self.parent.id()), LOG_LEVEL_WARN)
       elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
         log(self, "Don't know how to print array of DiagnosticInfo in node " + str(self.parent.id()), LOG_LEVEL_WARN)
-      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
-        log(self, "Don't know how to print array of Extensionobject in node " + str(self.parent.id()), LOG_LEVEL_WARN)
       elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_STATUSCODE:
         log(self, "Don't know how to print array of StatusCode in node " + str(self.parent.id()), LOG_LEVEL_WARN)
       else:
+        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+          for v in self.value:
+            log(self, "Building extObj array index " + str(self.value.index(v)))
+            code = code + v.printOpen62541CCode_SubType_build(arrayIndex=self.value.index(v))
         code.append("UA_Variant *" + self.parent.getCodePrintableID() + "_variant = UA_Variant_new();")
+        code.append(self.parent.getCodePrintableID() + "_variant->type = &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "];")
         code.append("UA_" + self.value[0].stringRepresentation + " " + valueName + "[" + str(len(self.value)) + "];")
-        for v in self.value:
-          code.append(valueName + "[" + str(self.value.index(v)) + "] = " + v.printOpen62541CCode_SubType() + ";")
+        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+          for v in self.value:
+            log(self, "Printing extObj array index " + str(self.value.index(v)))
+            code.append(valueName + "[" + str(self.value.index(v)) + "] = " + v.printOpen62541CCode_SubType(asIndirect=False) + ";")
+            code.append("UA_free(" + v.printOpen62541CCode_SubType() + ");")
+        else:
+          for v in self.value:
+            code.append(valueName + "[" + str(self.value.index(v)) + "] = " + v.printOpen62541CCode_SubType() + ";")
         code.append("UA_Variant_setArrayCopy(" + self.parent.getCodePrintableID() + "_variant, &" + valueName + ", (UA_Int32) " + str(len(self.value)) + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
         code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
     else:
@@ -314,17 +364,25 @@ class opcua_value_t():
         log(self, "Don't know how to print scalar DateTime in node " + str(self.parent.id()), LOG_LEVEL_WARN)
       elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
         log(self, "Don't know how to print scalar DiagnosticInfo in node " + str(self.parent.id()), LOG_LEVEL_WARN)
-      elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
-        log(self, "Don't know how to print scalar ExtensionObject in node " + str(self.parent.id()), LOG_LEVEL_WARN)
       elif self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_STATUSCODE:
         log(self, "Don't know how to print scalar StatusCode in node " + str(self.parent.id()), LOG_LEVEL_WARN)
       else:
         # The following strategy applies to all other types, in particular strings and numerics.
+        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+          code = code + self.value[0].printOpen62541CCode_SubType_build()
         code.append("UA_Variant *" + self.parent.getCodePrintableID() + "_variant = UA_Variant_new();")
-        code.append("UA_" + self.value[0].stringRepresentation + " " + valueName + " = " + self.value[0].printOpen62541CCode_SubType() + ";")
-        code.append("UA_Variant_setScalarCopy(" + self.parent.getCodePrintableID() + "_variant, &" + valueName + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
-        code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(&" + valueName + ");")
-        code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
+        code.append(self.parent.getCodePrintableID() + "_variant->type = &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "];")
+        if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
+          code.append("UA_" + self.value[0].stringRepresentation + " *" + valueName + " = " + self.value[0].printOpen62541CCode_SubType() + ";")
+          code.append("UA_Variant_setScalarCopy(" + self.parent.getCodePrintableID() + "_variant, " + valueName + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
+          #FIXME: There is no membership definition for extensionObjects generated in this function.
+          code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(" + valueName + ");")
+          code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
+        else:
+          code.append("UA_" + self.value[0].stringRepresentation + " " + valueName + " = " + self.value[0].printOpen62541CCode_SubType() + ";")
+          code.append("UA_Variant_setScalarCopy(" + self.parent.getCodePrintableID() + "_variant, &" + valueName + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
+          code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(&" + valueName + ");")
+          code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
     return code
 
 
@@ -345,6 +403,106 @@ class opcua_BuiltinType_extensionObject_t(opcua_value_t):
       self.__typeId__ = data
     return self.__typeId__
 
+  def getCodeInstanceName(self):
+    return self.__codeInstanceName__
+
+  def setCodeInstanceName(self, recursionDepth, arrayIndex):
+    self.__inVariableRecursionDepth__ = recursionDepth
+    self.__inVariableArrayIndex__ = arrayIndex
+    self.__codeInstanceName__ = self.parent.getCodePrintableID() + "_" + str(self.alias()) + "_" + str(arrayIndex) + "_" + str(recursionDepth)
+    return self.__codeInstanceName__
+
+  def printOpen62541CCode_SubType_build(self, recursionDepth=0, arrayIndex=0):
+    code = [""]
+    codegen = open62541_MacroHelper();
+
+    log(self, "Building extensionObject for " + str(self.parent.id()))
+    log(self, "Value    " + str(self.value))
+    log(self, "Encoding " + str(self.getEncodingRule()))
+
+    self.setCodeInstanceName(recursionDepth, arrayIndex)
+    # If there are any ExtensionObjects instide this ExtensionObject, we need to
+    # generate one-time-structs for them too before we can proceed;
+    for subv in self.value:
+      if isinstance(subv, list):
+        log(self, "ExtensionObject contains an ExtensionObject, which is currently not encodable!", LOG_LEVEL_ERR)
+
+    code.append("struct {")
+    for field in self.getEncodingRule():
+      ptrSym = ""
+      # If this is an Array, this is pointer to its contents with a AliasOfFieldSize entry
+      if field[2] != 0:
+        code.append("  UA_Int32 " + str(field[0]) + "Size;")
+        ptrSym = "*"
+      if len(field[1]) == 1:
+        code.append("  UA_" + str(field[1][0]) + " " + ptrSym + str(field[0]) + ";")
+      else:
+        code.append("  UA_ExtensionObject " + " " + ptrSym + str(field[0]) + ";")
+    code.append("} " + self.getCodeInstanceName() + "_struct;")
+
+    # Assign data to the struct contents
+    # Track the encoding rule definition to detect arrays and/or ExtensionObjects
+    encFieldIdx = 0
+    for subv in self.value:
+      encField = self.getEncodingRule()[encFieldIdx]
+      encFieldIdx = encFieldIdx + 1;
+      log(self, "Encoding of field " + subv.alias() + " is " + str(subv.getEncodingRule()) + "defined by " + str(encField))
+      # Check if this is an array
+      if encField[2] == 0:
+        code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + " = " + subv.printOpen62541CCode_SubType(asIndirect=False) + ";")
+      else:
+        if isinstance(subv, list):
+          # this is an array
+          code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + "Size = " + str(len(subv)) + ";")
+          code.append(self.getCodeInstanceName()+"_struct."+subv.alias()+" = (UA_" + subv.stringRepresentation + " *) UA_malloc(sizeof(UA_" + subv.stringRepresentation + ")*"+ str(len(subv))+");")
+          log(self, "Encoding included array of " + str(len(subv)) + " values.")
+          for subvidx in range(0,len(subv)):
+            subvv = subv[subvidx]
+            log(self, "  " + str(subvix) + " " + str(subvv))
+            code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + "[" + str(subvidx) + "] = " + subvv.printOpen62541CCode_SubType(asIndirect=True) + ";")
+          code.append("}")
+        else:
+          code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + "Size = 1;")
+          code.append(self.getCodeInstanceName()+"_struct."+subv.alias()+" = (UA_" + subv.stringRepresentation + " *) UA_malloc(sizeof(UA_" + subv.stringRepresentation + "));")
+          code.append(self.getCodeInstanceName()+"_struct."+subv.alias() + "[0]  = " + subv.printOpen62541CCode_SubType(asIndirect=True) + ";")
+
+
+    # Allocate some memory
+    code.append("UA_ExtensionObject *" + self.getCodeInstanceName() + " =  UA_ExtensionObject_new();")
+    code.append(self.getCodeInstanceName() + "->encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;")
+    code.append(self.getCodeInstanceName() + "->typeId = UA_NODEID_NUMERIC(" + str(self.parent.dataType().target().id().ns) + ", " + str(self.parent.dataType().target().id().i) + "+ UA_ENCODINGOFFSET_BINARY);")
+    code.append("UA_ByteString_newMembers(&" + self.getCodeInstanceName() + "->body, 65000);" )
+
+    # Encode each value as a bytestring seperately.
+    code.append("size_t " + self.getCodeInstanceName() + "_encOffset = 0;" )
+    encFieldIdx = 0;
+    for subv in self.value:
+      encField = self.getEncodingRule()[encFieldIdx]
+      encFieldIdx = encFieldIdx + 1;
+      if encField[2] == 0:
+        code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + ", &" + self.getCodeInstanceName() + "->body, &" + self.getCodeInstanceName() + "_encOffset);" )
+      else:
+        if isinstance(subv, list):
+          for subvidx in range(0,len(subv)):
+            code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + "[" + str(subvidx) + "], &" + self.getCodeInstanceName() + "->body, &" + self.getCodeInstanceName() + "_encOffset);" )
+        else:
+          code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + "[0], &" + self.getCodeInstanceName() + "->body, &" + self.getCodeInstanceName() + "_encOffset);" )
+
+    # Reallocate the memory by swapping the 65k Bytestring for a new one
+    code.append(self.getCodeInstanceName() + "->body.length = " + self.getCodeInstanceName() + "_encOffset;");
+    code.append("UA_Byte *" + self.getCodeInstanceName() + "_newBody = (UA_Byte *) UA_malloc(" + self.getCodeInstanceName() + "_encOffset );" )
+    code.append("memcpy(" + self.getCodeInstanceName() + "_newBody, " + self.getCodeInstanceName() + "->body.data, " + self.getCodeInstanceName() + "_encOffset);" )
+    code.append("UA_Byte *" + self.getCodeInstanceName() + "_oldBody = " + self.getCodeInstanceName() + "->body.data;");
+    code.append(self.getCodeInstanceName() + "->body.data = " +self.getCodeInstanceName() + "_newBody;")
+    code.append("UA_free(" + self.getCodeInstanceName() + "_oldBody);")
+    code.append("")
+    return code
+
+  def printOpen62541CCode_SubType(self, asIndirect=True):
+    if asIndirect == False:
+      return "*" + str(self.getCodeInstanceName())
+    return str(self.getCodeInstanceName())
+
   def __str__(self):
     return "'" + self.alias() + "':" + self.stringRepresentation + "(" + str(self.value) + ")"
 
@@ -408,8 +566,11 @@ class opcua_BuiltinType_localizedtext_t(opcua_value_t):
       else:
         self.value.append(tmp[0].firstChild.data)
 
-  def printOpen62541CCode_SubType(self):
-      code = "UA_LOCALIZEDTEXT_ALLOC(\"" + str(self.value[0]) + "\", \"" + str(self.value[1].encode('utf-8')) + "\")"
+  def printOpen62541CCode_SubType(self, asIndirect=True):
+      if asIndirect==True:
+        code = "UA_LOCALIZEDTEXT_ALLOC(\"" + str(self.value[0]) + "\", \"" + str(self.value[1].encode('utf-8')) + "\")"
+      else:
+        code = "UA_LOCALIZEDTEXT(\"" + str(self.value[0]) + "\", \"" + str(self.value[1].encode('utf-8')) + "\")"
       return code
 
 class opcua_BuiltinType_expandednodeid_t(opcua_value_t):
@@ -426,7 +587,7 @@ class opcua_BuiltinType_expandednodeid_t(opcua_value_t):
 
     log(self, "Not implemented", LOG_LEVEL_ERR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     #FIXME! This one is definetely broken!
     code = ""
     return code
@@ -467,6 +628,22 @@ class opcua_BuiltinType_nodeid_t(opcua_value_t):
       if self.value == None:
         log(self, "Node with id " + str(unicode(xmlvalue.firstChild.data)) + " was not found in namespace.", LOG_LEVEL_ERROR)
 
+  def printOpen62541CCode_SubType(self, asIndirect=True):
+    if self.value == None:
+      return "UA_NODEID_NUMERIC(0,0)"
+    nodeId = self.value.id()
+    if nodeId.i != None:
+      return "UA_NODEID_NUMERIC(" + str(nodeId.ns) + ", " + str(nodeId.i) + ")"
+    elif nodeId.s != None:
+      return "UA_NODEID_STRING("  + str(nodeId.ns) + ", " + str(nodeId.s) + ")"
+    elif nodeId.b != None:
+      log(self, "NodeID Generation macro for bytestrings has not been implemented.")
+      return "UA_NODEID_NUMERIC(0,0)"
+    elif nodeId.g != None:
+      log(self, "NodeID Generation macro for guids has not been implemented.")
+      return "UA_NODEID_NUMERIC(0,0)"
+    return "UA_NODEID_NUMERIC(0,0)"
+
 class opcua_BuiltinType_datetime_t(opcua_value_t):
   def setStringReprentation(self):
     self.stringRepresentation = "DateTime"
@@ -550,7 +727,7 @@ class opcua_BuiltinType_qualifiedname_t(opcua_value_t):
         self.value = [0]
         self.value.append(unicode(xmlvalue.firstChild.data))
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
       code = "UA_QUALIFIEDNAME_ALLOC(" + str(self.value[0]) + ", \"" + self.value[1].encode('utf-8') + "\")"
       return code
 
@@ -654,7 +831,7 @@ class opcua_BuiltinType_boolean_t(opcua_value_t):
       else:
         self.value = True
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_byte_t(opcua_value_t):
@@ -688,7 +865,7 @@ class opcua_BuiltinType_byte_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_sbyte_t(opcua_value_t):
@@ -722,7 +899,7 @@ class opcua_BuiltinType_sbyte_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_int16_t(opcua_value_t):
@@ -756,7 +933,7 @@ class opcua_BuiltinType_int16_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_uint16_t(opcua_value_t):
@@ -790,7 +967,7 @@ class opcua_BuiltinType_uint16_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_int32_t(opcua_value_t):
@@ -824,7 +1001,7 @@ class opcua_BuiltinType_int32_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_uint32_t(opcua_value_t):
@@ -858,7 +1035,7 @@ class opcua_BuiltinType_uint32_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_int64_t(opcua_value_t):
@@ -888,7 +1065,7 @@ class opcua_BuiltinType_int64_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_uint64_t(opcua_value_t):
@@ -922,7 +1099,7 @@ class opcua_BuiltinType_uint64_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_float_t(opcua_value_t):
@@ -956,7 +1133,7 @@ class opcua_BuiltinType_float_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_double_t(opcua_value_t):
@@ -990,7 +1167,7 @@ class opcua_BuiltinType_double_t(opcua_value_t):
       except:
         log(self, "Error parsing integer. Expected " + self.stringRepresentation + " but got " + unicode(xmlvalue.firstChild.data), LOG_LEVEL_ERROR)
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_string_t(opcua_value_t):
@@ -1026,7 +1203,7 @@ class opcua_BuiltinType_string_t(opcua_value_t):
     else:
       self.value = str(unicode(xmlvalue.firstChild.data))
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
       code = "UA_STRING_ALLOC(\"" + self.value.encode('utf-8') + "\")"
       return code
 
@@ -1037,7 +1214,7 @@ class opcua_BuiltinType_xmlelement_t(opcua_BuiltinType_string_t):
   def setNumericRepresentation(self):
     self.__binTypeId__ = BUILTINTYPE_TYPEID_XMLELEMENT
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
       code = "UA_XMLELEMENT_ALLOC(\"" + self.value.encode('utf-8') + "\")"
       return code
 
@@ -1069,7 +1246,7 @@ class opcua_BuiltinType_bytestring_t(opcua_value_t):
     else:
       self.value = str(unicode(xmlvalue.firstChild.data))
 
-  def printOpen62541CCode_SubType(self):
+  def printOpen62541CCode_SubType(self, asIndirect=True):
       bs = ""
       for line in self.value:
         bs = bs + str(line).replace("\n","");

+ 0 - 1
tools/pyUANamespace/ua_namespace.py

@@ -532,7 +532,6 @@ class opcua_namespace():
     code.append('#include "'+outfilename+'.h"')
     code.append("inline void "+outfilename+"(UA_Server *server) {")
 
-
     # Find all references necessary to create the namespace and
     # "Bootstrap" them so all other nodes can safely use these referencetypes whenever
     # they can locate both source and target of the reference.

+ 31 - 16
tools/pyUANamespace/ua_node_types.py

@@ -135,6 +135,9 @@ class opcua_referencePointer_t():
       retval = retval + ">"
     return retval
 
+  def __repr__(self):
+      return self.__str__()
+
   def __cmp__(self, other):
     if not isinstance(other, opcua_referencePointer_t):
       return -1
@@ -639,6 +642,7 @@ class opcua_node_t:
     codegen = open62541_MacroHelper(supressGenerationOfAttribute=supressGenerationOfAttribute)
     code = []
     code.append("")
+    code.append("do {")
 
     # Just to be sure...
     if not (self in unPrintedNodes):
@@ -654,12 +658,21 @@ class opcua_node_t:
       if parent[1].referenceType() != None:
         code.append("// Referencing node found and declared as parent: " + str(parent[0].id()) + "/" + str(parent[0].__node_browseName__) + " using " + str(parent[1].referenceType().id()) + "/" + str(parent[1].referenceType().__node_browseName__))
         code.append("UA_Server_addNode(server, (UA_Node*) " + self.getCodePrintableID() + ", " + codegen.getCreateExpandedNodeIDMacro(parent[0]) + ", " + codegen.getCreateNodeIDMacro(parent[1].referenceType()) + ");")
+    
     # Otherwise use the "Bootstrapping" method and we will get registered
     # with other nodes later.
     else:
       code.append("// Parent node does not exist yet. This node will be bootstrapped and linked later.")
       code.append("UA_NodeStore_insert(server->nodestore, (UA_Node*) " + self.getCodePrintableID() + ", UA_NULL);")
-
+    # Note: getFirstParentNode will return [parentNode, referenceToChild]
+    (parentNode, parentRef) = self.getFirstParentNode()
+    if not (parentNode in unPrintedNodes) and (parentNode != None):
+      if parentRef.referenceType() != None:
+        code.append("// Referencing node found and declared as parent: " + str(parentNode .id()) + "/" + str(parentNode .__node_browseName__) + " using " + str(parentRef.referenceType().id()) + "/" + str(parentRef.referenceType().__node_browseName__))
+        code.append("UA_Server_addNode(server, (UA_Node*) " + self.getCodePrintableID() + ", " + codegen.getCreateExpandedNodeIDMacro(parentNode ) + ", " + codegen.getCreateNodeIDMacro(parentRef.referenceType()) + ");")
+        # Parent to child reference is added by the server, do not reprint that reference
+        if parentRef in unPrintedReferences:
+          unPrintedReferences.remove(parentRef)
     # Try to print all references to nodes that already exist
     # Note: we know the reference types exist, because the namespace class made sure they were
     #       the first ones being printed
@@ -700,6 +713,7 @@ class opcua_node_t:
       # This is necessery to make printing work at all!
       unPrintedNodes.remove(self)
 
+    code.append("} while(0);")
     return code
 
 class opcua_node_referenceType_t(opcua_node_t):
@@ -979,19 +993,18 @@ class opcua_node_variable_t(opcua_node_t):
 
     if self.historizing():
       code.append(self.getCodePrintableID() + "->historizing = UA_TRUE;")
-    #else:
-      #code.append(self.getCodePrintableID() + "->historizing = UA_FALSE;")
 
     code.append(self.getCodePrintableID() + "->minimumSamplingInterval = (UA_Double) " + str(self.minimumSamplingInterval()) + ";")
     code.append(self.getCodePrintableID() + "->userAccessLevel = (UA_Int32) " + str(self.userAccessLevel()) + ";")
     code.append(self.getCodePrintableID() + "->accessLevel = (UA_Int32) " + str(self.accessLevel()) + ";")
     code.append(self.getCodePrintableID() + "->valueRank = (UA_Int32) " + str(self.valueRank()) + ";")
 
-    # Delegate the encoding of the datavalue to the helper if we have
-    # determined a valid encoding
-    if self.dataType() != None and (isinstance(self.dataType().target(), opcua_node_dataType_t) and self.dataType().target().isEncodable()):
-      if self.value() != None:
-        code = code + self.value().printOpen62541CCode()
+    if self.dataType() != None and isinstance(self.dataType().target(), opcua_node_dataType_t):
+      # Delegate the encoding of the datavalue to the helper if we have
+      # determined a valid encoding
+      if self.dataType().target().isEncodable():
+        if self.value() != None:
+          code = code + self.value().printOpen62541CCode()
     return code
 
 class opcua_node_method_t(opcua_node_t):
@@ -1043,15 +1056,12 @@ class opcua_node_method_t(opcua_node_t):
 
   def printOpen62541CCode_Subtype(self):
     code = []
+
+    # UA_False is default for booleans on _init()
     if self.executable():
       code.append(self.getCodePrintableID() + "->executable = UA_TRUE;")
-    else:
-      code.append(self.getCodePrintableID() + "->executable = UA_FALSE;")
-
     if self.userExecutable():
       code.append(self.getCodePrintableID() + "->userExecutable = UA_TRUE;")
-    else:
-      code.append(self.getCodePrintableID() + "->userExecutable = UA_FALSE;")
 
     return code
 
@@ -1335,7 +1345,7 @@ class opcua_node_dataType_t(opcua_node_t):
               self.__encodable__ = False
               break
             else:
-              self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [self.browseName(), subenc]
+              self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [self.browseName(), subenc, 0]
       if len(self.__baseTypeEncoding__) == 0:
         log(self, prefix + "No viable definition for " + self.browseName() + " " + str(self.id()) + " found.")
         self.__encodable__ = False
@@ -1351,6 +1361,7 @@ class opcua_node_dataType_t(opcua_node_t):
 
     isEnum = True
     isSubType = True
+    hasValueRank = 0
 
     # We need to store the definition as ordered data, but can't use orderedDict
     # for backward compatibility with Python 2.6 and 3.4
@@ -1363,6 +1374,7 @@ class opcua_node_dataType_t(opcua_node_t):
         fname  = ""
         fdtype = ""
         enumVal = ""
+        hasValueRank = 0
         for at,av in x.attributes.items():
           if at == "DataType":
             fdtype = str(av)
@@ -1373,6 +1385,7 @@ class opcua_node_dataType_t(opcua_node_t):
             enumVal = int(av)
             isSubType = False
           elif at == "ValueRank":
+            hasValueRank = int(av)
             log(self, "Arrays or matrices (ValueRank) are not supported for datatypes. This DT will become scalar.", LOG_LEVEL_WARN)
           else:
             log(self, "Unknown Field Attribute " + str(at), LOG_LEVEL_WARN)
@@ -1399,10 +1412,12 @@ class opcua_node_dataType_t(opcua_node_t):
             # The node in the datatype element was found. we inherit its encoding,
             # but must still ensure that the dtnode is itself validly encodable
             typeDict.append([fname, dtnode])
-            fdtype = str(dtnode.browseName())
+            if hasValueRank < 0:
+              hasValueRank = 0
+            fdtype = str(dtnode.browseName()) + "+"*hasValueRank
             log(self,  prefix + fname + " : " + fdtype + " -> " + str(dtnode.id()))
             subenc = dtnode.buildEncoding(indent=indent+1)
-            self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [[fname, subenc]]
+            self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [[fname, subenc, hasValueRank]]
             if not dtnode.isEncodable():
               # If we inherit an encoding from an unencodable not, this node is
               # also not encodable