Forráskód Böngészése

Merge pull request #291 from acplt/service_call_implementation

commit efd4337064bab252472de657b01eded8309b25a0
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jul 7 18:41:40 2015 +0200

    checking for empty call requests and cosmetic improvements

commit e9efbb784d9fcde3dfb6ec7fb35f718c3619f5ea
Merge: 9c55fe6 60a46e6
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jul 7 17:13:22 2015 +0200

    Merge branch 'master' into service_call_implementation

commit 9c55fe640d837b4cbe7941b999584d3f56879e07
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jul 7 17:12:41 2015 +0200

    Squashed commit of the following:

    commit f2f5e97f913a2944c2ca0b699939c785769b1f8d
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Tue Jul 7 16:46:31 2015 +0200

        remove small memleaks during startup

    commit 23fc4a0c6b2e2cdfbfab8c71546030ff05554065
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Tue Jul 7 16:21:21 2015 +0200

        clean up the branch

    commit cb1cead03ca5c3cf3d26ec8ac5418d0c5f3921b9
    Merge: a3ad1e8 60a46e6
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Tue Jul 7 16:19:14 2015 +0200

        Merge branch 'master' into service_call_interface

        Conflicts:
        	tools/pyUANamespace/ua_node_types.py

    commit a3ad1e845b539a7bed64358b057a54fe7acb3e75
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Tue Jul 7 16:14:00 2015 +0200

        simplify methods API on the client side

    commit 60a46e6abf1a46d8b39fa302d8c5aa825da045a9
    Merge: 232bb4e 028ed26
    Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
    Date:   Tue Jul 7 15:17:08 2015 +0200

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

    commit 232bb4ecf76efdc18e9b23d420d48b75ecb6530a
    Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
    Date:   Tue Jul 7 15:16:43 2015 +0200

        Backported (parts of) 1b6331b6333a49286e273a49286e27 from service_call_implementation: Fix duplicate reference from parent to child being created by namespace generator.

    commit 028ed266a207950d2ec9d8dac3ad85925e381b79
    Author: ChristianFimmers <christian.fimmers@rwth-aachen.de>
    Date:   Mon Jul 6 10:39:49 2015 +0200

        Merge pull request #290

    commit 6b2e0c6c36ba6304a340eef0c212d7bf78f67b60
    Author: Stasik0 <github@stasik.com>
    Date:   Mon Jul 6 10:39:49 2015 +0200

        one more space removed from the tag, relates to #289

    commit fd71ca7fa29c41a1a4f72ffe35005b9661a31403
    Author: Stasik0 <github@stasik.com>
    Date:   Mon Jul 6 10:38:27 2015 +0200

        removing non-ascii literal, fixing amalgamation tag in case no git found, relates to #289

    commit 0b9b7095cafca2ff96bbab379395c9cc9e953240
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Fri Jul 3 17:54:43 2015 +0200

        no compiler flag required to compile amalgamated server

    commit 4d5ddc81f891c276887218227f97a7d7e64876fe
    Merge: 66caa20 6ff67cf
    Author: Sten Grüner <Stasik0@users.noreply.github.com>
    Date:   Thu Jul 2 09:10:57 2015 +0200

        Merge pull request #287 from acplt/simple_tcp

        simplfy closing connections in the network layer

    commit 6ff67cf40c7b4560136f0ab295743f83d3cb359c
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Wed Jul 1 20:48:51 2015 +0200

        simplfy closing connections in the network layer

        sockets are only closed in the main loop. if a worker wants to close a connection, it is shutdown.
        this is then picked up by the select-call in the next main loop iteration.
        when a connection is closed, two jobs are returned. one to immediately detach the securechannel and a delayed job that eventually frees the connection memory.

    commit 66caa20e93169ae4daaeac752ba62b45517b5018
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jul 1 08:39:19 2015 +0200

        fixing client blocking on server disonnect

    commit 54cb2b4a7dcc6fdb3fb65b0497da276919f5fe79
    Merge: 50512f4 435eaf9
    Author: Julius Pfrommer <jpfr@users.noreply.github.com>
    Date:   Tue Jun 30 21:45:42 2015 +0200

        Merge pull request #286 from acplt/externalNodestoreLeakFix

        fixed a memleak when using external namespaces

    commit 435eaf918d2f5532a255c6f4062ab1e736698401
    Author: LEvertz <l.evertz@plt.rwth-aachen.de>
    Date:   Tue Jun 30 17:04:59 2015 +0200

        added a function to delete external namespaces to the server --> fixes a
        memleak when using external namespaces

    commit 50512f48fe6679f615e806fe4d39af9b148ce6ff
    Author: Stasik0 <github@stasik.com>
    Date:   Mon Jun 29 09:55:52 2015 +0200

        added MSVC-builds to the readme

    commit a06f3ed9af3098690f3079ea80642c3f1287ecd9
    Author: FlorianPalm <palm@plt.rwth-aachen.de>
    Date:   Sun Jun 28 23:28:56 2015 +0200

        fixed a typo in README

    commit cee54343c09d99d4ddfcb6a106867d96f597b4f3
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sun Jun 28 17:53:53 2015 +0200

        restore calcSizeBinary as a single function UA_calcSizeBinary

    commit 462b813509806d2ffb606944874ccfbe213fb43b
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sun Jun 28 15:25:31 2015 +0200

        replace redundant functions with ifdef, some cosmetic cleanup

    commit 1652c19b8bb7ceba0a11c34ed933db2d4052c3a4
    Author: Julius Pfrommer <jpfr@users.noreply.github.com>
    Date:   Sun Jun 28 11:06:24 2015 +0200

        wrap coveralls. the service fails regularly

    commit 88cc9a3da2c5043d7a898c5b0577890e2235a584
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sun Jun 28 09:24:05 2015 +0200

        small documentation improvements

    commit 0f60259a7a9bfff0f9a603a2125b7d490631f211
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 23:42:40 2015 +0200

        install sphinx theme

    commit c2f87ed825265e9bc4442413168103c4ec2cb06f
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 23:26:26 2015 +0200

        add documentation generation with sphinx

    commit bc5151e65d34d25f04ca1fe75aeabf4e1f6e141c
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 17:43:42 2015 +0200

        improve documentation, get rid of a duplicate function for string comparison

    commit 5caa99787017dac826914c4d37954a73ba9e5e76
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 15:38:51 2015 +0200

        always copy data from the data source (removing the release function pointer)

    commit d0e09b24ffed3a9cbff9d34042dbdc4cd194fb9d
    Author: Stasik0 <github@stasik.com>
    Date:   Sat Jun 27 15:32:48 2015 +0200

        removing enforcing NameSpaceId in BrowseName, relates to #284

    commit a8bbad7e0adbfde590a41e6fbf9abe821eb56fab
    Author: Stasik0 <github@stasik.com>
    Date:   Sat Jun 27 11:53:11 2015 +0200

        64bit support + artifacts

    commit d4c98b6c4e36f8e00a96e9bf6e4650fff1d66de5
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 13:14:37 2015 +0200

        fix client amalgamation

    commit 6b52e3c5dc30c2b78fb5ab1b2a6feff80be3849c
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 11:58:17 2015 +0200

        add a define for multithreading

    commit df4753b00d74b21f8a76e644fc63d9e5d1564206
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Fri Jun 26 21:48:49 2015 +0200

        improve amalgamation

    commit 2c869f59ab9580477cf84bfffc95be9dc648858a
    Author: Stasik0 <github@stasik.com>
    Date:   Fri Jun 26 11:41:58 2015 +0200

        appveyor ci support, fixes #280

    commit de5cb7d32b21207534db86e156369d069b7c4cfd
    Author: FlorianPalm <palm@plt.rwth-aachen.de>
    Date:   Wed Jun 24 20:57:05 2015 +0200

        Update server.c

        removed instant test setup from server.c related to #265

    commit 1adbb44483473da7438605e91fef657593a3ee91
    Merge: 0632a83 494ac62
    Author: FlorianPalm <palm@plt.rwth-aachen.de>
    Date:   Wed Jun 24 18:00:18 2015 +0200

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

    commit 0632a8363b6364f011ce1366b1f9085046e1f97b
    Author: FlorianPalm <palm@plt.rwth-aachen.de>
    Date:   Wed Jun 24 17:59:02 2015 +0200

        related to #265, added another define as well as "faster" encoding functions for ARM7TDMI with "mixed" endian

    commit 494ac6234ecb19395b9edde89acb60d33f232ef6
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 17:25:44 2015 +0200

        typo

    commit 263644e8fbc6161fc866ef2b293e49ab2e23bca8
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 17:23:51 2015 +0200

        partial revert of CMake changes

    commit 6f95b1146d2a3e1138a397a6c6713853980ba5e4
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 17:18:25 2015 +0200

        fixing compile dependencies

    commit b3dc6cff718c814eb63adff1756b499c24a9cb53
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 16:57:44 2015 +0200

        masking out non-windows parts of the example - get rid of F_OK completely

    commit f23534441816cacc08479b392209f218d006a8cc
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 16:42:29 2015 +0200

        adding dependencies in CMake

    commit b2277cfc2bec6ec58bc953efda6a981c91b2a97f
    Author: Sten Grüner <Stasik0@users.noreply.github.com>
    Date:   Wed Jun 24 16:10:07 2015 +0200

        F_OK is now direclty defined in server.c

commit 203ef3503807776015d4db8d257e63dc1b948f29
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jul 7 14:53:35 2015 +0200

    fix two memory bugs

commit 7791f6e455a348a9b9acd9ae76b7776364fffeee
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Thu Jul 2 12:07:36 2015 +0200

    remove private headers from the example server. simplify the callback signature

commit 5a623caab225aa6e86de76341ef9973d65d9ef82
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 30 17:42:52 2015 +0200

    In/Out Variable nodes now get a random ID on creation (thank to @Stasik0 for the patch in master)

commit d853a343924a153f05b6bcb16abb959a47e5dd09
Merge: f7f9ed3 67840c9
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 30 17:39:17 2015 +0200

    Merge branch 'master' into service_call_implementation

    Conflicts:
    	examples/client.c
    	include/ua_client.h
    	src/client/ua_client.c
    	src/server/ua_server_binary.c

commit f7f9ed359f43f9a06c10a7b717441fd2e156c75c
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 30 17:33:41 2015 +0200

    Service_Call now checks whether Arguments encoded in the InputArguments variable of a method is an extensionObject or an Argument and picks the appropriate strategy (ExtensionObjects should ultimatively be removed once the namespace compilers find out how).

commit 3a5f33b1537df99e1ef0e33c8f81aa17b77a1e4a
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 30 17:12:23 2015 +0200

    Recreated UA_Server_addMethodNode for userspace operations. FIXME: Cannot be called, service_call needs to evade extensionObject decoding when arguments are not stored as extensionObjects.

commit 33dd54bf091f8c5b626b0b16d1b6333a49286e27
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Mon Jun 29 19:33:06 2015 +0200

    Renamed serviceCall headers into more appropriate form (still holding I/O data for the actual function call). Service call now checks the structure of passed arguments; return argument checking is still on the TODO list.

commit c93008c60603306fc1f0e15cb6d645699f5b543c
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Mon Jun 29 16:06:25 2015 +0200

    Python compiler can now compile flat extension objects (i.e. extObj's that don't contain other extObj's) directly from XML. This allows for any Arguments to be shown when calling methods. FIXME: There are preparations in the extObj printCode function that will allow hierarchic encodings of extObjs. Note also that identifying arrays inside extObj structs is not extensively tested.

commit f0fc16c5697f89d9c33da42e7f7de12e73bc67ab
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 25 21:03:39 2015 +0200

    Modifications in namespace compiler now allow basic scalar extensionObjects to be read and enconded from XML files. WARNING: ExtensionObjects containing ExtensionObjects and arrays > 1 element will propably fail to encode.

commit d78aab3cd29dea1115600a07271d5b9896c219f7
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Wed Jun 24 18:18:25 2015 +0200

    Added methods base structure for testing argument conformance to the methods definition.

commit bdb0efe99bd94bfd565075a0cc57410c750dbbd7
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Wed Jun 24 17:51:30 2015 +0200

    Revert "Use UA_Argument for method calls. A memleak remains in the argument datasource."

    This reverts commit b1a2edcbdf6821e58e99031d8b96c7ccd45c01a2.

commit b1a2edcbdf6821e58e99031d8b96c7ccd45c01a2
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jun 23 22:42:08 2015 +0200

    Use UA_Argument for method calls. A memleak remains in the argument datasource.

commit 13148053bf70b65e74198c4a43750795f2868349
Merge: 256d2fb 68252e5
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jun 23 19:45:31 2015 +0200

    Merge branch 'master' into service_call_implementation

commit 256d2fbdd190a4e4f685f4946ddbba9ce49cc9a3
Merge: 7ae87bc 2b2a866
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jun 23 17:26:58 2015 +0200

    Merge branch 'master' into service_call_implementation

commit 7ae87bc3bdf58511c05cf58b5a62a6d152a2c1b4
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Fri Jun 19 10:14:46 2015 +0200

    Added missing #ifdef ENABLE_METHODCALLS in example server.

commit be08b2d01b00950815af71730541d6757b126ee3
Merge: 728ae56 b515f7d
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 18:37:39 2015 +0200

    Merge branch 'master' into service_call_implementation

    Conflicts:
    	src/server/ua_server_binary.c

commit 728ae565465a6d61936030f7cba538b3095f5893
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 18:31:51 2015 +0200

    Commented on UA_NodeAttachedMethod currently _not_ being a dynamic struct (even though _new() exists). This may change in case the current implementation is lacking.

commit e59ee567f2e20cb1db46d3acacf964b06c5b8c57
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 17:33:17 2015 +0200

    Detaching methods from nodes now possible; attaching a method sets/unsets the executable bit

commit 2c02507427ff7880579cf7d807a74d61f1473b27
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 17:29:53 2015 +0200

    Removed List from UA_NodeAttachedMethod; UA_MethodNode->attachedMethod is now a direct struct; Attaching methods now replaces node for Multithreading safety.

commit c4379a0a9ac151543ec35c1e727f3d202d886e18
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 14:14:03 2015 +0200

    Methods are no longer being decorated by a MethodCall Manager. Instead MethodType nodes hold a UA_NodeAttachedMethod struct directly as per @jpfr's and @Stasik0's request. NodeAttachedMethod is still necessary, as otherwise the const attribute of the UA_Node is not maintained.

commit 4eeb09e507164508f173f1680340e6f77c6060b6
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 12:09:34 2015 +0200

    Slightly changed clientside UA_Client_CallMethod() function to return a statuscode (equal to callStatus also contained in outArgs), with outArgs now being set as a pointer-pointer.

commit 4b641ee683f03a6da512885ddb43e94b06efe658
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 11:22:03 2015 +0200

    Server/Client return call argument statuscodes to userspace; Separated function call return status from argument status in ArgumentList. Actually working client example.

commit 7f1e4d8a9b1ed11f4aa247cf82975d2940611f49
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 10:47:51 2015 +0200

    Statuscodes and returned arguments are now being made available to the client in the outputargs struct.

commit c319932e99270c89f5d8d5754929fc669fb67cb6
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 10:25:11 2015 +0200

    Fixed several (really stupid) memory allocation errors on the serverside.

commit e6713751a1dcc6b29a719d949f10280510eed933
Merge: 3d38228 a4cd3c5
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Wed Jun 17 18:58:04 2015 +0200

    Merge branch 'master' into service_call_implementation

    Conflicts:
    	tools/generate_datatypes.py

commit 3d38228995beff954a456f0e5546c15ca1cc8987
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Wed Jun 17 18:54:30 2015 +0200

    Server now executes userspace function when the call service is invoked. Basic scaffolding for clientside support put in place.

commit bf40c249c3a096c5a207b88d33c414d3a0bc2027
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 16 16:16:56 2015 +0200

    Cumulative commit for enabling methodCalls service set (cmake -DENABLE_METHODCALLS); creating/remove method hooks is still WIP.
ichrispa 9 éve
szülő
commit
f4629df441

+ 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

@@ -96,6 +96,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
@@ -172,7 +195,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;

+ 32 - 1
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;
@@ -272,7 +281,29 @@ int main(int argc, char** argv) {
         UA_Server_addVariableNode(server, arrayvar, myIntegerName, UA_NODEID_NUMERIC(1, ++id),
                                   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 */
     /**************/

+ 69 - 0
src/server/ua_server_addressspace.c

@@ -370,9 +370,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;

+ 5 - 0
src/server/ua_services.h

@@ -290,4 +290,9 @@ UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
 /** @} */
 /** @} */
 
+#ifdef ENABLE_METHODCALLS
+void Service_Call(UA_Server *server, UA_Session *session,
+                  const UA_CallRequest *request,
+                  UA_CallResponse *response);
+#endif
 #endif /* UA_SERVICES_H_ */

+ 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);
+    }
+}

+ 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.

+ 21 - 15
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):
@@ -709,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):
@@ -988,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):
@@ -1052,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
 
@@ -1344,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
@@ -1360,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
@@ -1372,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)
@@ -1382,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)
@@ -1408,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