Kaynağa Gözat

Merge branch '0.2'

Julius Pfrommer 8 yıl önce
ebeveyn
işleme
e6cf0d16ea

+ 2 - 4
CMakeLists.txt

@@ -92,9 +92,7 @@ if(CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang")
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DARWIN_C_SOURCE=1")
   endif()
 elseif(MSVC)
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3 /WX") # Compiler warnings, error on warning
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D_CRT_SECURE_NO_WARNINGS") # don't give warnings for scanf and printf
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D_WINSOCK_DEPRECATED_NO_WARNINGS") # inet_ntoa is deprecated but used for compatibility
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3 /WX") # Compiler warnings, error on warning
   set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT")
   set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd")
 endif()
@@ -223,8 +221,8 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_subscription.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel_subscriptions.c
                 # plugins and dependencies
-                ${PROJECT_SOURCE_DIR}/plugins/ua_clock.c
                 ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
+                ${PROJECT_SOURCE_DIR}/plugins/ua_clock.c
                 ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
                 ${PROJECT_SOURCE_DIR}/plugins/ua_config_standard.c
                 ${PROJECT_SOURCE_DIR}/deps/libc_time.c

+ 90 - 56
appveyor.yml

@@ -1,56 +1,90 @@
-version: '{build}'
-os: Visual Studio 2015 RC
-clone_folder: c:\projects\open62541
-environment:
-    global:
-        CYG_ROOT: C:/cygwin
-before_build:
-# Workaround for CMake not wanting sh.exe on PATH for MinGW
-- set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
-- set PATH=C:\MinGW\bin;%PATH%
-# Update Cygwin
-- ps: $exePath = "C:\cygwin\setup-x86.exe"; (New-Object Net.WebClient).DownloadFile('https://cygwin.com/setup-x86.exe', $exePath)
-build_script: 
-- cd c:\projects\open62541
-# cygwin cmake stopped working on 05.07.2016 -- commented out until a fix appears
-#- md build
-#- cd build
-#- echo "Testing cygwin"
-#- C:\cygwin\setup-x86.exe -q -P cmake,gcc-core,make,python
-#- cmd: '%CYG_ROOT%/bin/bash --login -lc "cd /cygdrive/c/projects/open62541/build; cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -G\"Unix Makefiles\" ..; make"'
-#- cd ..
-#- rd /s /q build
-- md build
-- cd build
-- echo "Testing MinGW32"
-- cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -G"MinGW Makefiles" ..
-- mingw32-make
-- cd ..
-- rd /s /q build
-- md build
-- cd build
-- cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -G"Visual Studio 12 2013" ..
-- msbuild open62541.sln
-- echo "Testing amalgamation"
-- cd ..
-- rd /s /q build
-- md build
-- cd build
-- cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013" ..
-- msbuild open62541.sln 
-- copy C:\projects\open62541\build\open62541.c C:\projects\open62541\build\Debug\open62541.c
-- copy C:\projects\open62541\build\open62541.h C:\projects\open62541\build\Debug\open62541.h
-- cd ..
-- echo "Win 64 build"
-- md build64
-- cd build64
-- cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013 Win64" ..
-- msbuild open62541.sln 
-- copy C:\projects\open62541\build64\open62541.c C:\projects\open62541\build64\Debug\open62541.c
-- copy C:\projects\open62541\build64\open62541.h C:\projects\open62541\build64\Debug\open62541.h
-- cd ..
-after_build:
-- 7z a open62541-win32.zip %APPVEYOR_BUILD_FOLDER%\build\Debug\*
-- 7z a open62541-win64.zip %APPVEYOR_BUILD_FOLDER%\build64\Debug\*
-- appveyor PushArtifact open62541-win32.zip
-- appveyor PushArtifact open62541-win64.zip
+version: '{build}'
+
+os: Visual Studio 2015 RC
+
+clone_folder: c:\projects\open62541
+clone_depth: 20
+
+environment:
+    global:
+        CYG_ARCH: x86
+        CYG_ROOT: C:/cygwin
+        CYG_SETUP_URL: http://cygwin.com/setup-x86.exe
+        CYG_MIRROR: http://cygwin.mirror.constant.com
+        CYG_CACHE: C:\cygwin\var\cache\setup
+        CYG_BASH: C:/cygwin/bin/bash
+
+cache:
+  - '%CYG_CACHE%'
+
+#
+# Initialisation prior to pulling the Mono repository
+# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail
+#
+init:
+  - git config --global core.autocrlf input
+
+
+#
+# Install needed build dependencies
+#
+install:
+  - if not exist "%CYG_ROOT%" mkdir "%CYG_ROOT%"
+  - ps:  echo "Installing Cygwin from $env:CYG_SETUP_URL to $env:CYG_ROOT/setup-x86.exe"
+  - appveyor DownloadFile %CYG_SETUP_URL% -FileName %CYG_ROOT%/setup-x86.exe
+  - ps:  echo "Downloaded. Now ready to install."
+  - cmd: '"%CYG_ROOT%/setup-x86.exe" --quiet-mode --no-shortcuts --only-site -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" --packages cmake,gcc-core,make,python'
+  - cmd: '%CYG_BASH% -lc "cygcheck -dc cygwin"'
+
+
+before_build:
+  # Workaround for CMake not wanting sh.exe on PATH for MinGW
+  - set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
+  - set PATH=C:\MinGW\bin;%PATH%
+
+build_script: 
+  - cd c:\projects\open62541
+  - md build
+  - cd build
+  # cygwin cmake stopped working on 05.07.2016 -- commented out until a fix appears
+  #- echo "Testing cygwin"
+  #- '%CYG_BASH% --login -lc "/usr/bin/cmake.exe --version"'
+  #- '%CYG_BASH% --login -lc "cd /cygdrive/c/projects/open62541/build; cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -G\"Unix Makefiles\" .."'
+  #- '%CYG_BASH% --login -lc "cd /cygdrive/c/projects/open62541/build; make -j"'
+  - cd ..
+  - rd /s /q build
+  - md build
+  - cd build
+  - echo "Testing MinGW32"
+  - cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -G"MinGW Makefiles" ..
+  - mingw32-make
+  - cd ..
+  - rd /s /q build
+  - md build
+  - cd build
+  - cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -G"Visual Studio 12 2013" ..
+  - msbuild open62541.sln /m
+  - echo "Testing amalgamation"
+  - cd ..
+  - rd /s /q build
+  - md build
+  - cd build
+  - cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013" ..
+  - msbuild open62541.sln /m
+  - copy C:\projects\open62541\build\open62541.c C:\projects\open62541\build\Debug\open62541.c
+  - copy C:\projects\open62541\build\open62541.h C:\projects\open62541\build\Debug\open62541.h
+  - cd ..
+  - echo "Win 64 build"
+  - md build64
+  - cd build64
+  - cmake -DUA_BUILD_EXAMPLESERVER:BOOL=ON -DUA_BUILD_EXAMPLECLIENT:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013 Win64" ..
+  - msbuild open62541.sln /m
+  - copy C:\projects\open62541\build64\open62541.c C:\projects\open62541\build64\Debug\open62541.c
+  - copy C:\projects\open62541\build64\open62541.h C:\projects\open62541\build64\Debug\open62541.h
+  - cd ..
+
+after_build:
+  - 7z a open62541-win32.zip %APPVEYOR_BUILD_FOLDER%\build\Debug\*
+  - 7z a open62541-win64.zip %APPVEYOR_BUILD_FOLDER%\build64\Debug\*
+  - appveyor PushArtifact open62541-win32.zip
+  - appveyor PushArtifact open62541-win64.zip

+ 5 - 5
doc/tutorial_noderelations.rst

@@ -1,16 +1,16 @@
-Generating an OPC UA Information Model from XML Descriptions
-============================================================
+4. Generating an OPC UA Information Model from XML Descriptions
+===============================================================
 
 In the past tutorials you have learned to compile the stack in various configurations, create/delete nodes and manage variables. The core of OPC UA is its data modelling capabilities, and you will invariably find yourself confronted to investigate these relations during runtime. This tutorial will show you how to interact with object and type hierarchies and how to create your own.
 
 Compile XML Namespaces
 ----------------------
 
-So far we have made due with the hardcoded mini-namespace in the server stack. When writing an application, it is more then likely that you will want to create your own data models using some comfortable GUI based tools like UA Modeller. Most tools can export data to the OPC UA XML specification. open62541 contains a python based namespace compiler that can embed datamodels contained in XML files into the server stack.
+So far we have made due with the hardcoded mini-namespace in the server stack. When writing an application, it is more then likely that you will want to create your own data models using some comfortable GUI based tools like UA Modeler. Most tools can export data to the OPC UA XML specification. open62541 contains a python based namespace compiler that can embed datamodels contained in XML files into the server stack.
 
 Note beforehand that the pyUANamespace compiler you can find in the *tools* subfolder is *not* a XML transformation tool but a compiler. That means that it will create an internal representation (dAST) when parsing the XML files and attempt to understand this representation in order to generate C Code. In consequence, the compiler will refuse to print any inconsistencies or invalid nodes.
 
-As an example, we will create a simple object model using UA Modeller and embed this into the servers nodeset, which is exported to the following XML file:
+As an example, we will create a simple object model using UA Modeler and embed this into the servers nodeset, which is exported to the following XML file:
 
 .. code-block:: xml
 
@@ -234,7 +234,7 @@ Always make sure that your XML file comes *after* namespace 0. Also, take into c
   * Adding the relative path to the file into CMakeLists.txt
   * Compiling the stack
 
-After adding you XML file to CMakeLists.txt, rerun cmake in your build directory and enable ``DENABLE_GENERATE_NAMESPACE0``. Make especially sure that you are using the option ``CMAKE_BUILD_TYPE=Debug``. The generated namespace contains more than 30000 lines of code and many strings. Optimizing this amount of code with -O2 or -Os options will require several hours on most PCs! Also make sure to enable ``-DENABLE_METHODCALLS``, as namespace 0 does contain methods that need to be encoded::
+After adding your XML file to CMakeLists.txt, rerun cmake in your build directory and enable ``DENABLE_GENERATE_NAMESPACE0``. Make especially sure that you are using the option ``CMAKE_BUILD_TYPE=Debug``. The generated namespace contains more than 30000 lines of code and many strings. Optimizing this amount of code with -O2 or -Os options will require several hours on most PCs! Also make sure to enable ``-DENABLE_METHODCALLS``, as namespace 0 does contain methods that need to be encoded::
   
   ichrispa@Cassandra:open62541/build> cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_METHODCALLS=On -BUILD_EXAMPLECLIENT=On -BUILD_EXAMPLESERVER=On -DENABLE_GENERATE_NAMESPACE0=On ../
   -- Git version: v0.1.0-RC4-403-g198597c-dirty

+ 113 - 324
examples/server.c

@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
 #ifdef _MSC_VER
 # include <io.h> //access
 #else
@@ -36,16 +37,14 @@
 #include <pthread.h>
 #endif
 
-/****************************/
-/* Server-related variables */
-/****************************/
-
 UA_Boolean running = 1;
 UA_Logger logger = UA_Log_Stdout;
+static void stopHandler(int sign) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C");
+    running = 0;
+}
 
-/*************************/
-/* Read-only data source */
-/*************************/
+/* Datasource Example */
 static UA_StatusCode
 readTimeData(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
              const UA_NumericRange *range, UA_DataValue *value) {
@@ -64,228 +63,36 @@ readTimeData(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
     return UA_STATUSCODE_GOOD;
 }
 
-/*****************************/
-/* Read-only CPU temperature */
-/*      Only on Linux        */
-/*****************************/
-FILE* temperatureFile = NULL;
-static UA_StatusCode
-readTemperature(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
-                const UA_NumericRange *range, UA_DataValue *value) {
-    if(range) {
-        value->hasStatus = true;
-        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
-        return UA_STATUSCODE_GOOD;
-    }
-
-    rewind(temperatureFile);
-    fflush(temperatureFile);
-
-    UA_Double currentTemperature;
-    if(fscanf(temperatureFile, "%lf", &currentTemperature) != 1){
-        UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "Can not parse temperature");
-        exit(1);
-    }
-
-    currentTemperature /= 1000.0;
-
-    value->sourceTimestamp = UA_DateTime_now();
-    value->hasSourceTimestamp = true;
-    UA_Variant_setScalarCopy(&value->value, &currentTemperature, &UA_TYPES[UA_TYPES_DOUBLE]);
-    value->hasValue = true;
-    return UA_STATUSCODE_GOOD;
-}
-
-/*************************/
-/* Read-write status led */
-/*************************/
-#ifdef UA_ENABLE_MULTITHREADING
-pthread_rwlock_t writeLock;
-#endif
-FILE* triggerFile = NULL;
-FILE* ledFile = NULL;
-UA_Boolean ledStatus = 0;
-
-static UA_StatusCode
-readLedStatus(void *handle, UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
-              const UA_NumericRange *range, UA_DataValue *value) {
-    if(range)
-        return UA_STATUSCODE_BADINDEXRANGEINVALID;
-
-    value->hasValue = true;
-    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &ledStatus,
-                                                    &UA_TYPES[UA_TYPES_BOOLEAN]);
-
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-
-    if(sourceTimeStamp) {
-        value->sourceTimestamp = UA_DateTime_now();
-        value->hasSourceTimestamp = true;
-    }
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-writeLedStatus(void *handle, const UA_NodeId nodeid,
-               const UA_Variant *data, const UA_NumericRange *range) {
-    if(range)
-        return UA_STATUSCODE_BADINDEXRANGEINVALID;
-
-#ifdef UA_ENABLE_MULTITHREADING
-    pthread_rwlock_wrlock(&writeLock);
-#endif
-    if(data->data)
-        ledStatus = *(UA_Boolean*)data->data;
-
-    if(triggerFile)
-        fseek(triggerFile, 0, SEEK_SET);
-
-    if(ledFile) {
-        if(ledStatus == 1)
-            fprintf(ledFile, "%s", "1");
-        else
-            fprintf(ledFile, "%s", "0");
-        fflush(ledFile);
-    }
-#ifdef UA_ENABLE_MULTITHREADING
-    pthread_rwlock_unlock(&writeLock);
-#endif
-    return UA_STATUSCODE_GOOD;
-}
-
+/* Method Node Example */
 #ifdef UA_ENABLE_METHODCALLS
 static UA_StatusCode
-getMonitoredItems(void *methodHandle, const UA_NodeId objectId,
-                  size_t inputSize, const UA_Variant *input,
-                  size_t outputSize, UA_Variant *output) {
-    UA_String tmp = UA_STRING("Hello World");
-    UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
+helloWorld(void *methodHandle, const UA_NodeId objectId,
+           size_t inputSize, const UA_Variant *input,
+           size_t outputSize, UA_Variant *output) {
+    /* input is a scalar string (checked by the server) */
+    UA_String *name = (UA_String*)input[0].data;
+    UA_String hello = UA_STRING("Hello ");
+    UA_String greet;
+    greet.length = hello.length + name->length;
+    greet.data = malloc(greet.length);
+    memcpy(greet.data, hello.data, hello.length);
+    memcpy(greet.data + hello.length, name->data, name->length);
+    UA_Variant_setScalarCopy(output, &greet, &UA_TYPES[UA_TYPES_STRING]);
+    UA_String_deleteMembers(&greet);
     return UA_STATUSCODE_GOOD;
 }
 #endif
 
-static void stopHandler(int sign) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C");
-    running = 0;
-}
-
-static UA_ByteString loadCertificate(void) {
-    UA_ByteString certificate = UA_STRING_NULL;
-    FILE *fp = NULL;
-    if(!(fp=fopen("server_cert.der", "rb"))) {
-        errno = 0; // we read errno also from the tcp layer...
-        return certificate;
-    }
-
-    fseek(fp, 0, SEEK_END);
-    certificate.length = (size_t)ftell(fp);
-    certificate.data = malloc(certificate.length*sizeof(UA_Byte));
-    if(!certificate.data){
-        fclose(fp);
-        return certificate;
-    }
-
-    fseek(fp, 0, SEEK_SET);
-    if(fread(certificate.data, sizeof(UA_Byte), certificate.length, fp) < (size_t)certificate.length)
-        UA_ByteString_deleteMembers(&certificate); // error reading the cert
-    fclose(fp);
-
-    return certificate;
-}
-
-static UA_StatusCode
-nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-instantiationHandle(const UA_NodeId newNodeId, const UA_NodeId templateId, void *handle ) {
-  printf("Instantiated Node ns=%d; id=%d from ns=%d; id=%d\n", newNodeId.namespaceIndex,
-         newNodeId.identifier.numeric, templateId.namespaceIndex, templateId.identifier.numeric);
-  return UA_STATUSCODE_GOOD;
-}
-
 int main(int argc, char** argv) {
     signal(SIGINT, stopHandler); /* catches ctrl-c */
-#ifdef UA_ENABLE_MULTITHREADING
-    pthread_rwlock_init(&writeLock, 0);
-#endif
 
     UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
     UA_ServerConfig config = UA_ServerConfig_standard;
-    config.serverCertificate = loadCertificate();
     config.networkLayers = &nl;
     config.networkLayersSize = 1;
     UA_Server *server = UA_Server_new(config);
 
-    // add node with the datetime data source
-    UA_DataSource dateDataSource = (UA_DataSource) {.handle = NULL, .read = readTimeData, .write = NULL};
-    UA_VariableAttributes v_attr;
-    UA_VariableAttributes_init(&v_attr);
-    v_attr.description = UA_LOCALIZEDTEXT("en_US","current time");
-    v_attr.displayName = UA_LOCALIZEDTEXT("en_US","current time");
-    v_attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
-    const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
-    UA_NodeId dataSourceId;
-    UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), dateName,
-                                        UA_NODEID_NULL, v_attr, dateDataSource, &dataSourceId);
-
-#ifndef _WIN32
-    /* cpu temperature monitoring for linux machines */
-    const char *temperatureFileName = "/sys/class/thermal/thermal_zone0/temp"; // RaspberryPi
-    // const char *temperatureFileName = "/sys/class/hwmon/hwmon0/device/temp1_input"; // Beaglebone
-    // const char *temperatureFileName = "/sys/class/thermal/thermal_zone3/temp"; // Intel Edison Alternative 1
-    // const char *temperatureFileName = "/sys/class/thermal/thermal_zone4/temp"; // Intel Edison Alternative 2
-    if((temperatureFile = fopen(temperatureFileName, "r"))) {
-        // add node with the data source
-        UA_DataSource temperatureDataSource = (UA_DataSource) {
-            .handle = NULL, .read = readTemperature, .write = NULL};
-        const UA_QualifiedName tempName = UA_QUALIFIEDNAME(1, "cpu temperature");
-        UA_VariableAttributes_init(&v_attr);
-        v_attr.description = UA_LOCALIZEDTEXT("en_US","temperature");
-        v_attr.displayName = UA_LOCALIZEDTEXT("en_US","temperature");
-        v_attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
-        UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
-                                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), tempName,
-                                            UA_NODEID_NULL, v_attr, temperatureDataSource, NULL);
-    }
-
-    /* LED control for rpi */
-    if(access("/sys/class/leds/led0/trigger", F_OK ) != -1 ||
-       access("/sys/class/leds/led0/brightness", F_OK ) != -1) {
-        if((triggerFile = fopen("/sys/class/leds/led0/trigger", "w")) &&
-           (ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
-            //setting led mode to manual
-            fprintf(triggerFile, "%s", "none");
-            fflush(triggerFile);
-
-            //turning off led initially
-            fprintf(ledFile, "%s", "1");
-            fflush(ledFile);
-
-            // add node with the LED status data source
-            UA_DataSource ledStatusDataSource = (UA_DataSource) {
-                .handle = NULL, .read = readLedStatus, .write = writeLedStatus};
-            UA_VariableAttributes_init(&v_attr);
-            v_attr.description = UA_LOCALIZEDTEXT("en_US","status LED");
-            v_attr.displayName = UA_LOCALIZEDTEXT("en_US","status LED");
-            v_attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
-            const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
-            UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
-                                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), statusName,
-                                                UA_NODEID_NULL, v_attr, ledStatusDataSource, NULL);
-        } else
-            UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND,
-                           "[Raspberry Pi] LED file exist, but is not accessible (try to run server with sudo)");
-    }
-#endif
-
-    // add a static variable node to the adresspace
+    /* add a static variable node to the server */
     UA_VariableAttributes myVar;
     UA_VariableAttributes_init(&myVar);
     myVar.description = UA_LOCALIZEDTEXT("en_US", "the answer");
@@ -297,53 +104,92 @@ int main(int argc, char** argv) {
     const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    /* Instantiation Callback can be used when a typeDefinition creates new nodes under this added one.
-     * The method will be called for each created node.
-     */
-    UA_InstantiationCallback theAnswerCallback = {.method=instantiationHandle, .handle=(void*) server};
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
-                              myIntegerName, UA_NODEID_NULL, myVar, &theAnswerCallback, NULL);
+        myIntegerName, UA_NODEID_NULL, myVar, NULL, NULL);
     UA_Variant_deleteMembers(&myVar.value);
 
-    /**************/
-    /* Demo Nodes */
-    /**************/
+    /* add a variable with the datetime data source */
+    UA_DataSource dateDataSource = (UA_DataSource) {.handle = NULL, .read = readTimeData, .write = NULL};
+    UA_VariableAttributes v_attr;
+    UA_VariableAttributes_init(&v_attr);
+    v_attr.description = UA_LOCALIZEDTEXT("en_US","current time");
+    v_attr.displayName = UA_LOCALIZEDTEXT("en_US","current time");
+    v_attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
+    const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
+    UA_NodeId dataSourceId;
+    UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), dateName,
+                                        UA_NODEID_NULL, v_attr, dateDataSource, &dataSourceId);
+
+    /* Add HelloWorld method to the server */
+#ifdef UA_ENABLE_METHODCALLS
+    UA_Argument inputArguments;
+    UA_Argument_init(&inputArguments);
+    inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    inputArguments.description = UA_LOCALIZEDTEXT("en_US", "Say your name");
+    inputArguments.name = UA_STRING("Name");
+    inputArguments.valueRank = -1; /* scalar argument */
 
+    UA_Argument outputArguments;
+    UA_Argument_init(&outputArguments);
+    outputArguments.arrayDimensionsSize = 0;
+    outputArguments.arrayDimensions = NULL;
+    outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    outputArguments.description = UA_LOCALIZEDTEXT("en_US", "Receive a greeting");
+    outputArguments.name = UA_STRING("greeting");
+    outputArguments.valueRank = -1;
+
+    UA_MethodAttributes addmethodattributes;
+    UA_MethodAttributes_init(&addmethodattributes);
+    addmethodattributes.displayName = UA_LOCALIZEDTEXT("en_US", "Hello World");
+    addmethodattributes.executable = true;
+    addmethodattributes.userExecutable = true;
+    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, 62541),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+        UA_QUALIFIEDNAME(1, "hello_world"), addmethodattributes,
+        &helloWorld, /* callback of the method node */
+        NULL, /* handle passed with the callback */
+        1, &inputArguments, 1, &outputArguments, NULL);
+#endif
+
+    /* Add folders for demo information model */
 #define DEMOID 50000
+#define SCALARID 50001
+#define ARRAYID 50002
+#define MATRIXID 50003
+#define DEPTHID 50004
+
     UA_ObjectAttributes object_attr;
     UA_ObjectAttributes_init(&object_attr);
-    object_attr.description = UA_LOCALIZEDTEXT("en_US","Demo");
-    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Demo");
+    object_attr.description = UA_LOCALIZEDTEXT("en_US", "Demo");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Demo");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEMOID),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Demo"),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
+        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Demo"),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
-#define SCALARID 50001
-    object_attr.description = UA_LOCALIZEDTEXT("en_US","Scalar");
-    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Scalar");
+    object_attr.description = UA_LOCALIZEDTEXT("en_US", "Scalar");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Scalar");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, SCALARID),
-                            UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                            UA_QUALIFIEDNAME(1, "Scalar"),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
+        UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        UA_QUALIFIEDNAME(1, "Scalar"),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
-#define ARRAYID 50002
-    object_attr.description = UA_LOCALIZEDTEXT("en_US","Array");
-    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Array");
+    object_attr.description = UA_LOCALIZEDTEXT("en_US", "Array");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Array");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, ARRAYID),
-                            UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                            UA_QUALIFIEDNAME(1, "Array"),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
+        UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        UA_QUALIFIEDNAME(1, "Array"),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
-#define MATRIXID 50003
-    object_attr.description = UA_LOCALIZEDTEXT("en_US","Matrix");
-    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Matrix");
+    object_attr.description = UA_LOCALIZEDTEXT("en_US", "Matrix");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Matrix");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(1, DEMOID),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Matrix"),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
-
-    /** Fill demo nodes of different types **/
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Matrix"),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
+    /* Fill demo nodes for each type*/
     UA_UInt32 id = 51000; // running id in namespace 0
     for(UA_UInt32 type = 0; type < UA_TYPES_DIAGNOSTICINFO; type++) {
         if(type == UA_TYPES_VARIANT || type == UA_TYPES_DIAGNOSTICINFO)
@@ -352,7 +198,11 @@ int main(int argc, char** argv) {
         UA_VariableAttributes attr;
         UA_VariableAttributes_init(&attr);
         char name[15];
+#if defined(_WIN32) && !defined(__MINGW32__)
+        sprintf_s(name, 15, "%02d", type);
+#else
         sprintf(name, "%02d", type);
+#endif
         attr.displayName = UA_LOCALIZEDTEXT("en_US",name);
         attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
         attr.writeMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION;
@@ -363,18 +213,15 @@ int main(int argc, char** argv) {
         void *value = UA_new(&UA_TYPES[type]);
         UA_Variant_setScalar(&attr.value, value, &UA_TYPES[type]);
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
-                                  UA_NODEID_NUMERIC(1, SCALARID),
-                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                  UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                   qualifiedName, UA_NODEID_NULL, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
 
         /* add an array node for every built-in type */
-        UA_Variant_setArray(&attr.value, UA_Array_new(10, &UA_TYPES[type]),
-                            10, &UA_TYPES[type]);
-        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
-                                  UA_NODEID_NUMERIC(1, ARRAYID),
-                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                  qualifiedName, UA_NODEID_NULL, attr, NULL, NULL);
+        UA_Variant_setArray(&attr.value, UA_Array_new(10, &UA_TYPES[type]), 10, &UA_TYPES[type]);
+        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, ARRAYID),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
+                                  UA_NODEID_NULL, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
 
         /* add an matrix node for every built-in type */
@@ -386,17 +233,14 @@ int main(int argc, char** argv) {
         attr.value.arrayLength = 9;
         attr.value.data = myMultiArray;
         attr.value.type = &UA_TYPES[type];
-        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
-                                  UA_NODEID_NUMERIC(1, MATRIXID),
-                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                  qualifiedName, UA_NODEID_NULL, attr, NULL, NULL);
+        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, MATRIXID),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
+                                  UA_NODEID_NULL, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
     }
 
-    /** Hierarchy of depth 10 with forward and inverse references **/
-    /** Enter node "depth9" in CTT configuration - Project->Settings->Server Test->NodeIds->Paths->Starting Node 1 **/
-
-#define DEPTHID 50004
+    /* Hierarchy of depth 10 for CTT testing with forward and inverse references */
+    /* Enter node "depth 9" in CTT configuration - Project->Settings->Server Test->NodeIds->Paths->Starting Node 1 */
     object_attr.description = UA_LOCALIZEDTEXT("en_US","DepthDemo");
     object_attr.displayName = UA_LOCALIZEDTEXT("en_US","DepthDemo");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEPTHID),
@@ -407,7 +251,11 @@ int main(int argc, char** argv) {
     id = DEPTHID; // running id in namespace 0 - Start with Matrix NODE
     for(UA_UInt32 i = 1; i <= 20; i++) {
         char name[15];
+#if defined(_WIN32) && !defined(__MINGW32__)
+        sprintf_s(name, 15, "depth%i", i);
+#else
         sprintf(name, "depth%i", i);
+#endif
         object_attr.description = UA_LOCALIZEDTEXT("en_US",name);
         object_attr.displayName = UA_LOCALIZEDTEXT("en_US",name);
         UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, id+i),
@@ -416,40 +264,6 @@ int main(int argc, char** argv) {
                                 UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
     }
 
-#ifdef UA_ENABLE_METHODCALLS
-    UA_Argument inputArguments;
-    UA_Argument_init(&inputArguments);
-    inputArguments.arrayDimensionsSize = 0;
-    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 = 0;
-    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_MethodAttributes addmethodattributes;
-    UA_MethodAttributes_init(&addmethodattributes);
-    addmethodattributes.description = UA_LOCALIZEDTEXT("en_US", "Return a single argument as passed by the caller");
-    addmethodattributes.displayName = UA_LOCALIZEDTEXT("en_US", "ping");
-    addmethodattributes.executable = true;
-    addmethodattributes.userExecutable = true;
-    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                            UA_QUALIFIEDNAME(1,"ping"), addmethodattributes,
-                            &getMonitoredItems, // Call this method
-                            (void *) server,    // Pass our server pointer as a handle to the method
-                            1, &inputArguments, 1, &outputArguments, NULL);
-#endif
-
     /* Add the variable to some more places to get a node with three inverse references for the CTT */
     UA_ExpandedNodeId answer_nodeid = UA_EXPANDEDNODEID_STRING(1, "the.answer");
     UA_Server_addReference(server, UA_NODEID_NUMERIC(1, DEMOID),
@@ -457,38 +271,13 @@ int main(int argc, char** argv) {
     UA_Server_addReference(server, UA_NODEID_NUMERIC(1, SCALARID),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), answer_nodeid, true);
 
-    // Example for iterating over all nodes referenced by "Objects":
-    //printf("Nodes connected to 'Objects':\n=============================\n");
-    UA_Server_forEachChildNodeCall(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), nodeIter, NULL);
-
-    // Some easy localization
+    /* Example for manually setting an attribute within the server */
     UA_LocalizedText objectsName = UA_LOCALIZEDTEXT("en_US", "Objects");
     UA_Server_writeDisplayName(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), objectsName);
 
-    //start server
-    UA_StatusCode retval = UA_Server_run(server, &running); //blocks until running=false
-
-    //ctrl-c received -> clean up
+    /* run server */
+    UA_StatusCode retval = UA_Server_run(server, &running); /* run until ctrl-c is received */
     UA_Server_delete(server);
     nl.deleteMembers(&nl);
-
-    if(temperatureFile)
-        fclose(temperatureFile);
-
-    if(triggerFile) {
-        fseek(triggerFile, 0, SEEK_SET);
-        //setting led mode to default
-        fprintf(triggerFile, "%s", "mmc0");
-        fclose(triggerFile);
-    }
-
-    if(ledFile)
-        fclose(ledFile);
-
-#ifdef UA_ENABLE_MULTITHREADING
-    pthread_rwlock_destroy(&writeLock);
-#endif
-
-    UA_ByteString_deleteMembers(&config.serverCertificate);
     return (int)retval;
 }

+ 3 - 0
examples/server_nodeset.xml

@@ -39,5 +39,8 @@
             <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
             <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=5001</Reference>
         </References>
+        <Value>
+            <uax:Double>42.0</uax:Double>
+        </Value>
     </UAVariable>
 </UANodeSet>

+ 1 - 1
include/ua_connection.h

@@ -75,7 +75,7 @@ struct UA_Connection {
     UA_Int32 sockfd;                 /* Most connectivity solutions run on
                                         sockets. Having the socket id here
                                         simplifies the design. */
-    void *handle;                    /* A pointer to the networklayer */
+    void *handle;                    /* A pointer to internal data */
     UA_ByteString incompleteMessage; /* A half-received message (TCP is a
                                         streaming protocol) is stored here */
 

+ 4 - 1
plugins/ua_clock.c

@@ -4,7 +4,10 @@
 #include "ua_types.h"
 
 #include <time.h>
-#if defined(_WIN32)
+#ifdef _WIN32
+# ifdef SLIST_ENTRY
+#  undef SLIST_ENTRY /* Fix redefinition of SLIST_ENTRY on mingw winnt.h */
+# endif
 # include <windows.h>
 #else
 # include <sys/time.h>

+ 5 - 0
plugins/ua_network_tcp.c

@@ -19,6 +19,11 @@
 #include <errno.h>
 #ifdef _WIN32
 # include <malloc.h>
+# ifdef SLIST_ENTRY
+#  undef SLIST_ENTRY /* Fix redefinition of SLIST_ENTRY on mingw winnt.h */
+# endif
+# define _WINSOCK_DEPRECATED_NO_WARNINGS /* inet_ntoa is deprecated on MSVC but used for compatibility */
+# include <winsock2.h>
 # include <ws2tcpip.h>
 # define CLOSESOCKET(S) closesocket((SOCKET)S)
 # define ssize_t int

+ 59 - 42
src/client/ua_client.c

@@ -16,9 +16,11 @@
 
 static void UA_Client_init(UA_Client* client, UA_ClientConfig config) {
     client->state = UA_CLIENTSTATE_READY;
-    UA_Connection_init(&client->connection);
-    UA_SecureChannel_init(&client->channel);
-    client->channel.connection = &client->connection;
+    client->connection = UA_malloc(sizeof(UA_Connection));
+    UA_Connection_init(client->connection);
+    client->channel = UA_malloc(sizeof(UA_SecureChannel));
+    UA_SecureChannel_init(client->channel);
+    client->channel->connection = client->connection;
     UA_String_init(&client->endpointUrl);
     client->requestId = 0;
 
@@ -50,8 +52,10 @@ UA_Client * UA_Client_new(UA_ClientConfig config) {
 
 static void UA_Client_deleteMembers(UA_Client* client) {
     UA_Client_disconnect(client);
-    UA_Connection_deleteMembers(&client->connection);
-    UA_SecureChannel_deleteMembersCleanup(&client->channel);
+    UA_SecureChannel_deleteMembersCleanup(client->channel);
+    UA_free(client->channel);
+    UA_Connection_deleteMembers(client->connection);
+    UA_free(client->connection);
     if(client->endpointUrl.data)
         UA_String_deleteMembers(&client->endpointUrl);
     UA_UserTokenPolicy_deleteMembers(&client->token);
@@ -98,7 +102,7 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
     UA_TcpHelloMessage hello;
     UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
 
-    UA_Connection *conn = &client->connection;
+    UA_Connection *conn = client->connection;
     hello.maxChunkCount = conn->localConf.maxChunkCount;
     hello.maxMessageSize = conn->localConf.maxMessageSize;
     hello.protocolVersion = conn->localConf.protocolVersion;
@@ -107,7 +111,7 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
 
     UA_ByteString message;
     UA_StatusCode retval;
-    retval = client->connection.getSendBuffer(&client->connection, client->connection.remoteConf.recvBufferSize, &message);
+    retval = client->connection->getSendBuffer(client->connection, client->connection->remoteConf.recvBufferSize, &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -118,12 +122,12 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
     retval |= UA_TcpMessageHeader_encodeBinary(&messageHeader, &message, &offset);
     UA_TcpHelloMessage_deleteMembers(&hello);
     if(retval != UA_STATUSCODE_GOOD) {
-        client->connection.releaseSendBuffer(&client->connection, &message);
+        client->connection->releaseSendBuffer(client->connection, &message);
         return retval;
     }
 
     message.length = messageHeader.messageSize;
-    retval = client->connection.send(&client->connection, &message);
+    retval = client->connection->send(client->connection, &message);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK, "Sending HEL failed");
         return retval;
@@ -134,8 +138,8 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
     UA_ByteString_init(&reply);
     UA_Boolean realloced = false;
     do {
-        retval = client->connection.recv(&client->connection, &reply, client->config.timeout);
-        retval |= UA_Connection_completeMessages(&client->connection, &reply, &realloced);
+        retval = client->connection->recv(client->connection, &reply, client->config.timeout);
+        retval |= UA_Connection_completeMessages(client->connection, &reply, &realloced);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK, "Receiving ACK message failed");
             return retval;
@@ -147,7 +151,7 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
     UA_TcpAcknowledgeMessage ackMessage;
     retval = UA_TcpAcknowledgeMessage_decodeBinary(&reply, &offset, &ackMessage);
     if(!realloced)
-        client->connection.releaseRecvBuffer(&client->connection, &reply);
+        client->connection->releaseRecvBuffer(client->connection, &reply);
     else
         UA_ByteString_deleteMembers(&reply);
 
@@ -176,19 +180,19 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     if(renew && client->scRenewAt - UA_DateTime_now() > 0)
         return UA_STATUSCODE_GOOD;
 
-    UA_Connection *c = &client->connection;
+    UA_Connection *c = client->connection;
     if(c->state != UA_CONNECTION_ESTABLISHED)
         return UA_STATUSCODE_BADSERVERNOTCONNECTED;
 
     UA_SecureConversationMessageHeader messageHeader;
     messageHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
     if(renew)
-        messageHeader.secureChannelId = client->channel.securityToken.channelId;
+        messageHeader.secureChannelId = client->channel->securityToken.channelId;
     else
         messageHeader.secureChannelId = 0;
 
     UA_SequenceHeader seqHeader;
-    seqHeader.sequenceNumber = ++client->channel.sendSequenceNumber;
+    seqHeader.sequenceNumber = ++client->channel->sendSequenceNumber;
     seqHeader.requestId = ++client->requestId;
 
     UA_AsymmetricAlgorithmSecurityHeader asymHeader;
@@ -211,7 +215,7 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to open a SecureChannel");
     }
 
-    UA_ByteString_copy(&client->channel.clientNonce, &opnSecRq.clientNonce);
+    UA_ByteString_copy(&client->channel->clientNonce, &opnSecRq.clientNonce);
     opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
 
     UA_ByteString message;
@@ -234,12 +238,12 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
     UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
     if(retval != UA_STATUSCODE_GOOD) {
-        client->connection.releaseSendBuffer(&client->connection, &message);
+        client->connection->releaseSendBuffer(client->connection, &message);
         return retval;
     }
 
     message.length = messageHeader.messageHeader.messageSize;
-    retval = client->connection.send(&client->connection, &message);
+    retval = client->connection->send(client->connection, &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -280,6 +284,10 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     else
         UA_ByteString_deleteMembers(&reply);
 
+    //save the sequence number from server
+    client->channel->receiveSequenceNumber = seqHeader.sequenceNumber;
+
+
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                      "Decoding OpenSecureChannelResponse failed");
@@ -298,10 +306,10 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
             (UA_DateTime)(response.securityToken.revisedLifetime * (UA_Double)UA_MSEC_TO_DATETIME * 0.75);
 
         /* Replace the old nonce */
-        UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken);
-        UA_ChannelSecurityToken_copy(&response.securityToken, &client->channel.securityToken);
-        UA_ByteString_deleteMembers(&client->channel.serverNonce);
-        UA_ByteString_copy(&response.serverNonce, &client->channel.serverNonce);
+        UA_ChannelSecurityToken_deleteMembers(&client->channel->securityToken);
+        UA_ChannelSecurityToken_copy(&response.securityToken, &client->channel->securityToken);
+        UA_ByteString_deleteMembers(&client->channel->serverNonce);
+        UA_ByteString_copy(&response.serverNonce, &client->channel->serverNonce);
 
         if(renew)
             UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel renewed");
@@ -454,7 +462,7 @@ static UA_StatusCode SessionHandshake(UA_Client *client) {
     UA_NodeId_copy(&client->authenticationToken, &request.requestHeader.authenticationToken);
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timeoutHint = 10000;
-    UA_ByteString_copy(&client->channel.clientNonce, &request.clientNonce);
+    UA_ByteString_copy(&client->channel->clientNonce, &request.clientNonce);
     request.requestedSessionTimeout = 1200000;
     request.maxResponseMessageSize = UA_INT32_MAX;
 
@@ -488,7 +496,7 @@ static UA_StatusCode CloseSession(UA_Client *client) {
 }
 
 static UA_StatusCode CloseSecureChannel(UA_Client *client) {
-    UA_SecureChannel *channel = &client->channel;
+    UA_SecureChannel *channel = client->channel;
     UA_CloseSecureChannelRequest request;
     UA_CloseSecureChannelRequest_init(&request);
     request.requestHeader.requestHandle = ++client->requestHandle;
@@ -498,7 +506,7 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
 
     UA_SecureConversationMessageHeader msgHeader;
     msgHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_CLO + UA_CHUNKTYPE_FINAL;
-    msgHeader.secureChannelId = client->channel.securityToken.channelId;
+    msgHeader.secureChannelId = client->channel->securityToken.channelId;
 
     UA_SymmetricAlgorithmSecurityHeader symHeader;
     symHeader.tokenId = channel->securityToken.tokenId;
@@ -510,7 +518,7 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
     UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_CLOSESECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY);
 
     UA_ByteString message;
-    UA_Connection *c = &client->connection;
+    UA_Connection *c = client->connection;
     UA_StatusCode retval = c->getSendBuffer(c, c->remoteConf.recvBufferSize, &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
@@ -528,11 +536,11 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
 
     if(retval == UA_STATUSCODE_GOOD) {
         message.length = msgHeader.messageHeader.messageSize;
-        retval = client->connection.send(&client->connection, &message);
+        retval = client->connection->send(client->connection, &message);
     } else {
-        client->connection.releaseSendBuffer(&client->connection, &message);
+        client->connection->releaseSendBuffer(client->connection, &message);
     }
-    client->connection.close(&client->connection);
+    client->connection->close(client->connection);
     return retval;
 }
 
@@ -547,9 +555,9 @@ UA_Client_getEndpoints(UA_Client *client, const char *serverUrl,
 
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    client->connection = client->config.connectionFunc(UA_ConnectionConfig_standard, serverUrl,
+    *client->connection = client->config.connectionFunc(UA_ConnectionConfig_standard, serverUrl,
                                                        client->config.logger);
-    if(client->connection.state != UA_CONNECTION_OPENING) {
+    if(client->connection->state != UA_CONNECTION_OPENING) {
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
         goto cleanup;
     }
@@ -560,7 +568,7 @@ UA_Client_getEndpoints(UA_Client *client, const char *serverUrl,
         goto cleanup;
     }
 
-    client->connection.localConf = client->config.localConnectionConfig;
+    client->connection->localConf = client->config.localConnectionConfig;
     retval = HelAckHandshake(client);
     if(retval == UA_STATUSCODE_GOOD)
         retval = SecureChannelHandshake(client, false);
@@ -593,8 +601,8 @@ UA_Client_connect(UA_Client *client, const char *endpointUrl) {
     }
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    client->connection = client->config.connectionFunc(UA_ConnectionConfig_standard, endpointUrl, client->config.logger);
-    if(client->connection.state != UA_CONNECTION_OPENING) {
+    *client->connection = client->config.connectionFunc(UA_ConnectionConfig_standard, endpointUrl, client->config.logger);
+    if(client->connection->state != UA_CONNECTION_OPENING) {
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
         goto cleanup;
     }
@@ -605,7 +613,7 @@ UA_Client_connect(UA_Client *client, const char *endpointUrl) {
         goto cleanup;
     }
 
-    client->connection.localConf = client->config.localConnectionConfig;
+    client->connection->localConf = client->config.localConnectionConfig;
     retval = HelAckHandshake(client);
     if(retval == UA_STATUSCODE_GOOD)
         retval = SecureChannelHandshake(client, false);
@@ -616,7 +624,7 @@ UA_Client_connect(UA_Client *client, const char *endpointUrl) {
     if(retval == UA_STATUSCODE_GOOD)
         retval = ActivateSession(client);
     if(retval == UA_STATUSCODE_GOOD) {
-        client->connection.state = UA_CONNECTION_ESTABLISHED;
+        client->connection->state = UA_CONNECTION_ESTABLISHED;
         client->state = UA_CLIENTSTATE_CONNECTED;
     } else {
         goto cleanup;
@@ -633,11 +641,11 @@ UA_StatusCode UA_Client_disconnect(UA_Client *client) {
         return UA_STATUSCODE_BADNOTCONNECTED;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     /* Is a session established? */
-    if(client->channel.connection->state == UA_CONNECTION_ESTABLISHED &&
+    if(client->connection->state == UA_CONNECTION_ESTABLISHED &&
        !UA_NodeId_equal(&client->authenticationToken, &UA_NODEID_NULL))
         retval = CloseSession(client);
     /* Is a secure channel established? */
-    if(client->channel.connection->state == UA_CONNECTION_ESTABLISHED)
+    if(client->connection->state == UA_CONNECTION_ESTABLISHED)
         retval |= CloseSecureChannel(client);
     return retval;
 }
@@ -678,7 +686,7 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
     UA_UInt32 requestId = ++client->requestId;
     UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
                  "Sending a request of type %i", requestType->typeId.identifier.numeric);
-    retval = UA_SecureChannel_sendBinaryMessage(&client->channel, requestId, request, requestType);
+    retval = UA_SecureChannel_sendBinaryMessage(client->channel, requestId, request, requestType);
     if(retval != UA_STATUSCODE_GOOD) {
         if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
             respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE;
@@ -694,8 +702,8 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
     UA_ByteString_init(&reply);
     UA_Boolean realloced = false;
     do {
-        retval = client->connection.recv(&client->connection, &reply, client->config.timeout);
-        retval |= UA_Connection_completeMessages(&client->connection, &reply, &realloced);
+        retval = client->connection->recv(client->connection, &reply, client->config.timeout);
+        retval |= UA_Connection_completeMessages(client->connection, &reply, &realloced);
         if(retval != UA_STATUSCODE_GOOD) {
             respHeader->serviceResult = retval;
             client->state = UA_CLIENTSTATE_ERRORED;
@@ -718,6 +726,15 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
     if(retval != UA_STATUSCODE_GOOD)
         goto finish;
 
+    /* Does the sequence number match? */
+    retval = UA_SecureChannel_processSequenceNumber(seqHeader.sequenceNumber, client->channel);
+    if (retval != UA_STATUSCODE_GOOD){
+        UA_LOG_INFO_CHANNEL(client->config.logger, client->channel,
+                            "The sequence number was not increased by one. Got %i, expected %i",
+                            seqHeader.sequenceNumber, client->channel->receiveSequenceNumber + 1);
+        goto finish;
+    }
+
     /* Todo: we need to demux responses since a publish responses may come at any time */
     if(!UA_NodeId_equal(&responseId, &expectedNodeId) || seqHeader.requestId != requestId) {
         if(responseId.identifier.numeric != UA_NS0ID_SERVICEFAULT + UA_ENCODINGOFFSET_BINARY) {
@@ -738,7 +755,7 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
  finish:
     UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
     if(!realloced)
-        client->connection.releaseRecvBuffer(&client->connection, &reply);
+        client->connection->releaseRecvBuffer(client->connection, &reply);
     else
         UA_ByteString_deleteMembers(&reply);
 

+ 4 - 3
src/client/ua_client_internal.h

@@ -40,6 +40,7 @@ typedef struct UA_Client_Subscription_s {
     LIST_HEAD(UA_ListOfClientMonitoredItems, UA_Client_MonitoredItem_s) MonitoredItems;
 } UA_Client_Subscription;
 
+void UA_Client_Subscriptions_forceDelete(UA_Client *client, UA_Client_Subscription *sub);
 #endif
 
 /**********/
@@ -56,8 +57,8 @@ struct UA_Client {
     UA_ClientState state;
 
     /* Connection */
-    UA_Connection connection;
-    UA_SecureChannel channel;
+    UA_Connection *connection;
+    UA_SecureChannel *channel;
     UA_String endpointUrl;
     UA_UInt32 requestId;
 
@@ -82,6 +83,6 @@ struct UA_Client {
     UA_DateTime scRenewAt;
 };
 
-void UA_Client_Subscriptions_forceDelete(UA_Client *client, UA_Client_Subscription *sub);
+
 
 #endif /* UA_CLIENT_INTERNAL_H_ */

+ 75 - 0
src/server/ua_server.c

@@ -10,10 +10,18 @@
 #include "ua_namespaceinit_generated.h"
 #endif
 
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+#include "ua_subscription.h"
+#endif
+
 #if defined(UA_ENABLE_MULTITHREADING) && !defined(NDEBUG)
 UA_THREAD_LOCAL bool rcu_locked = false;
 #endif
 
+#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
+UA_THREAD_LOCAL UA_Session* methodCallSession = NULL;
+#endif
+
 static const UA_NodeId nodeIdHasSubType = {
     .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
     .identifier.numeric = UA_NS0ID_HASSUBTYPE};
@@ -440,6 +448,38 @@ addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32 variabletyp
     addNodeInternal(server, (UA_Node*)variabletype, UA_NODEID_NUMERIC(0, parent), nodeIdHasSubType);
 }
 
+#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
+static UA_StatusCode
+GetMonitoredItems(void *handle, const UA_NodeId objectId, size_t inputSize,
+                          const UA_Variant *input, size_t outputSize, UA_Variant *output) {
+    UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data));
+    UA_Session* session = methodCallSession;
+    UA_Subscription* subscription = UA_Session_getSubscriptionByID(session, subscriptionId);
+    if(!subscription)
+        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+
+    UA_UInt32 sizeOfOutput = 0;
+    UA_MonitoredItem* monitoredItem;
+    LIST_FOREACH(monitoredItem, &subscription->MonitoredItems, listEntry) {
+        sizeOfOutput++;
+    }
+    if(sizeOfOutput==0)
+        return UA_STATUSCODE_GOOD;
+
+    UA_UInt32* clientHandles = UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);
+    UA_UInt32* serverHandles = UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);
+    UA_UInt32 i = 0;
+    LIST_FOREACH(monitoredItem, &subscription->MonitoredItems, listEntry) {
+        clientHandles[i] = monitoredItem->clientHandle;
+        serverHandles[i] = monitoredItem->itemId;
+        i++;
+    }
+    UA_Variant_setArray(&output[0], clientHandles, sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);
+    UA_Variant_setArray(&output[1], serverHandles, sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]);
+    return UA_STATUSCODE_GOOD;
+}
+#endif
+
 UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_Server *server = UA_calloc(1, sizeof(UA_Server));
     if(!server)
@@ -1235,6 +1275,41 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT), nodeIdHasTypeDefinition,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), true);
 
+#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
+    UA_Argument inputArguments;
+    UA_Argument_init(&inputArguments);
+    inputArguments.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
+    inputArguments.name = UA_STRING("SubscriptionId");
+    inputArguments.valueRank = -1; /* scalar argument */
+
+    UA_Argument outputArguments[2];
+    UA_Argument_init(&outputArguments[0]);
+    outputArguments[0].dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
+    outputArguments[0].name = UA_STRING("ServerHandles");
+    outputArguments[0].valueRank = 1;
+
+    UA_Argument_init(&outputArguments[1]);
+    outputArguments[1].dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
+    outputArguments[1].name = UA_STRING("ClientHandles");
+    outputArguments[1].valueRank = 1;
+
+    UA_MethodAttributes addmethodattributes;
+    UA_MethodAttributes_init(&addmethodattributes);
+    addmethodattributes.displayName = UA_LOCALIZEDTEXT("", "GetMonitoredItems");
+    addmethodattributes.executable = true;
+    addmethodattributes.userExecutable = true;
+
+    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+        UA_QUALIFIEDNAME(0, "GetMonitoredItems"), addmethodattributes,
+        GetMonitoredItems, /* callback of the method node */
+        NULL, /* handle passed with the callback */
+        1, &inputArguments,
+        2, outputArguments,
+        NULL);
+#endif
+
     return server;
 }
 

+ 10 - 12
src/server/ua_server_binary.c

@@ -545,19 +545,17 @@ processMSG(UA_Connection *connection, UA_Server *server, const UA_TcpMessageHead
     }
 
     /* Does the sequence number match? */
-    if(sequenceHeader.sequenceNumber != channel->receiveSequenceNumber + 1) {
-        if(channel->receiveSequenceNumber + 1 > 4294966271 && sequenceHeader.sequenceNumber < 1024) {
-            channel->receiveSequenceNumber = sequenceHeader.sequenceNumber - 1; /* Roll over */
-        } else {
-            UA_LOG_INFO_CHANNEL(server->config.logger, channel,
-                                "The sequence number was not increased by one. Got %i, expected %i",
-                                sequenceHeader.sequenceNumber, channel->receiveSequenceNumber + 1);
-            sendError(channel, msg, *offset, &UA_TYPES[UA_TYPES_SERVICEFAULT],
-                      sequenceHeader.requestId, UA_STATUSCODE_BADSECURITYCHECKSFAILED);
-            return;
-        }
+    retval = UA_SecureChannel_processSequenceNumber(sequenceHeader.sequenceNumber, channel);
+    if (retval != UA_STATUSCODE_GOOD){
+        UA_LOG_INFO_CHANNEL(server->config.logger, channel,
+                            "The sequence number was not increased by one. Got %i, expected %i",
+                            sequenceHeader.sequenceNumber, channel->receiveSequenceNumber + 1);
+        sendError(channel, msg, *offset, &UA_TYPES[UA_TYPES_SERVICEFAULT],
+                  sequenceHeader.requestId, UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+        Service_CloseSecureChannel(server, channel);
+        connection->close(connection);
+        return;
     }
-    channel->receiveSequenceNumber++;
 
     /* Does the token match? */
     if(tokenId != channel->securityToken.tokenId) {

+ 5 - 0
src/server/ua_server_internal.h

@@ -33,6 +33,11 @@ typedef struct {
 } UA_Worker;
 #endif
 
+#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
+/* Internally used context to a session 'context' of the current mehtod call */
+extern UA_THREAD_LOCAL UA_Session* methodCallSession;
+#endif
+
 struct UA_Server {
     /* Meta */
     UA_DateTime startTime;

+ 9 - 0
src/server/ua_services_call.c

@@ -194,14 +194,23 @@ Service_Call_single(UA_Server *server, UA_Session *session, const UA_CallMethodR
     }
     result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength;
 
+#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
+    methodCallSession = session;
+#endif
+
     /* Call the method */
     result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, withObject->nodeId,
                                                       request->inputArgumentsSize, request->inputArguments,
                                                       result->outputArgumentsSize, result->outputArguments);
+
+#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
+    methodCallSession = NULL;
+#endif
     /* TODO: Verify Output Argument count, types and sizes */
 }
 void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request,
                   UA_CallResponse *response) {
+
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CallRequest");
     if(request->methodsToCallSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;

+ 14 - 0
src/server/ua_services_nodemanagement.c

@@ -879,6 +879,14 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
     inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
     inputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
     inputArgumentsVariableNode->valueRank = 1;
+    //TODO: 0.3 work item: the addMethodNode API does not have the possibility to set nodeIDs
+    //actually we need to change the signature to pass UA_NS0ID_SERVER_GETMONITOREDITEMS_INPUTARGUMENTS
+    //and UA_NS0ID_SERVER_GETMONITOREDITEMS_OUTPUTARGUMENTS into the function :/
+    if(result.addedNodeId.namespaceIndex == 0 &&
+       result.addedNodeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
+       result.addedNodeId.identifier.numeric == UA_NS0ID_SERVER_GETMONITOREDITEMS){
+        inputArgumentsVariableNode->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS_INPUTARGUMENTS);
+    }
     UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.variant.value, inputArguments,
                             inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
     UA_AddNodesResult inputAddRes;
@@ -896,6 +904,12 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
     outputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
     outputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
     outputArgumentsVariableNode->valueRank = 1;
+    //FIXME: comment in line 882
+    if(result.addedNodeId.namespaceIndex == 0 &&
+       result.addedNodeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
+       result.addedNodeId.identifier.numeric == UA_NS0ID_SERVER_GETMONITOREDITEMS){
+        outputArgumentsVariableNode->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS_OUTPUTARGUMENTS);
+    }
     UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.variant.value, outputArguments,
                             outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
     UA_AddNodesResult outputAddRes;

+ 1 - 1
src/ua_connection.c

@@ -18,8 +18,8 @@ void UA_Connection_init(UA_Connection *connection) {
     connection->handle = NULL;
     UA_ByteString_init(&connection->incompleteMessage);
     connection->send = NULL;
-    connection->close = NULL;
     connection->recv = NULL;
+    connection->close = NULL;
     connection->getSendBuffer = NULL;
     connection->releaseSendBuffer = NULL;
     connection->releaseRecvBuffer = NULL;

+ 13 - 0
src/ua_securechannel.c

@@ -332,3 +332,16 @@ void UA_SecureChannel_removeChunk(UA_SecureChannel *channel, UA_UInt32 requestId
         }
     }
 }
+
+UA_StatusCode UA_SecureChannel_processSequenceNumber (UA_UInt32 SequenceNumber, UA_SecureChannel *channel){
+/* Does the sequence number match? */
+    if(SequenceNumber != channel->receiveSequenceNumber + 1) {
+        if(channel->receiveSequenceNumber + 1 > 4294966271 && SequenceNumber < 1024) {
+            channel->receiveSequenceNumber = SequenceNumber - 1; /* Roll over */
+        } else {
+            return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+        }
+    }
+    channel->receiveSequenceNumber++;
+    return UA_STATUSCODE_GOOD;
+}

+ 1 - 0
src/ua_securechannel.h

@@ -77,6 +77,7 @@ UA_ByteString UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt3
 
 void UA_SecureChannel_removeChunk(UA_SecureChannel *channel, UA_UInt32 requestId);
 
+UA_StatusCode UA_SecureChannel_processSequenceNumber (UA_UInt32 SequenceNumber, UA_SecureChannel *channel);
 /**
  * Log Helper
  * ---------- */

+ 4 - 4
src/ua_types_encoding_binary.c

@@ -985,16 +985,16 @@ DataValue_decodeBinary(UA_DataValue *dst, const UA_DataType *_) {
         dst->hasSourceTimestamp = true;
         retval |= DateTime_decodeBinary(&dst->sourceTimestamp);
     }
-    if(encodingMask & 0x08) {
-        dst->hasServerTimestamp = true;
-        retval |= DateTime_decodeBinary(&dst->serverTimestamp);
-    }
     if(encodingMask & 0x10) {
         dst->hasSourcePicoseconds = true;
         retval |= UInt16_decodeBinary(&dst->sourcePicoseconds, NULL);
         if(dst->sourcePicoseconds > MAX_PICO_SECONDS)
             dst->sourcePicoseconds = MAX_PICO_SECONDS;
     }
+    if(encodingMask & 0x08) {
+        dst->hasServerTimestamp = true;
+        retval |= DateTime_decodeBinary(&dst->serverTimestamp);
+    }
     if(encodingMask & 0x20) {
         dst->hasServerPicoseconds = true;
         retval |= UInt16_decodeBinary(&dst->serverPicoseconds, NULL);

+ 0 - 8
src/ua_util.h

@@ -61,14 +61,6 @@
 /*************************/
 /* External Dependencies */
 /*************************/
-
-/* Fix redefinition of SLIST_ENTRY on windows */
-#ifdef _WIN32
-# include <winsock2.h>
-# include <windows.h>
-# undef SLIST_ENTRY
-#endif
-
 #include "queue.h"
 
 #ifdef UA_ENABLE_MULTITHREADING

+ 54 - 0
tests/check_client_subscriptions.c

@@ -73,12 +73,66 @@ START_TEST(Client_subscription) {
 }
 END_TEST
 
+START_TEST(Client_methodcall) {
+    UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
+    UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+    UA_UInt32 subId;
+    retval = UA_Client_Subscriptions_new(client, UA_SubscriptionSettings_standard, &subId);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+    /* monitor the server state */
+    UA_UInt32 monId;
+    retval = UA_Client_Subscriptions_addMonitoredItem(client, subId, UA_NODEID_NUMERIC(0, 2259),
+                                                      UA_ATTRIBUTEID_VALUE, NULL, NULL, &monId);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+    /* call a method to get monitored item id */
+    UA_Variant input;
+    UA_Variant_init(&input);
+    UA_Variant_setScalarCopy(&input, &subId, &UA_TYPES[UA_TYPES_UINT32]);
+    size_t outputSize;
+    UA_Variant *output;
+    retval = UA_Client_call(client, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS), 1, &input, &outputSize, &output);
+
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+    ck_assert_uint_eq(outputSize, 2);
+
+    ck_assert_uint_eq(output[0].arrayLength, 1);
+
+    ck_assert_uint_eq(*((UA_UInt32*)output[0].data), monId);
+
+    UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
+    UA_Variant_deleteMembers(&input);
+
+    /* call with invalid subscription id */
+    UA_Variant_init(&input);
+    subId = 0;
+    UA_Variant_setScalarCopy(&input, &subId, &UA_TYPES[UA_TYPES_UINT32]);
+    retval = UA_Client_call(client, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS), 1, &input, &outputSize, &output);
+
+    ck_assert_uint_eq(retval, UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID);
+
+    UA_Variant_deleteMembers(&input);
+
+    UA_Client_disconnect(client);
+    UA_Client_delete(client);
+}
+END_TEST
+
 static Suite* testSuite_Client(void) {
     Suite *s = suite_create("Client Subscription");
     TCase *tc_client = tcase_create("Client Subscription Basic");
     tcase_add_checked_fixture(tc_client, setup, teardown);
     tcase_add_test(tc_client, Client_subscription);
     suite_add_tcase(s,tc_client);
+    TCase *tc_client2 = tcase_create("Client Subscription + Method Call of GetMonitoredItmes");
+    tcase_add_checked_fixture(tc_client2, setup, teardown);
+    tcase_add_test(tc_client2, Client_methodcall);
+    suite_add_tcase(s,tc_client2);
     return s;
 }
 

+ 8 - 5
tools/pyUANamespace/generate_open62541CCode.py

@@ -67,14 +67,17 @@ parser.add_argument('-v','--verbose', action='count', help='Make the script more
 if __name__ == '__main__':
   args = parser.parse_args()
 
-  level= logging.CRITICAL
-  if (args.verbose==1):
+  level = logging.CRITICAL
+  verbosity = 0
+  if args.verbose:
+    verbosity = int(args.verbose)
+  if (verbosity==1):
     level = logging.ERROR
-  elif (args.verbose==2):
+  elif (verbosity==2):
     level = logging.WARNING
-  elif (args.verbose==3):
+  elif (verbosity==3):
     level = logging.INFO
-  elif (args.verbose>=4):
+  elif (verbosity>=4):
     level = logging.DEBUG
   logging.basicConfig(level=level)
   logger.setLevel(logging.INFO)

+ 59 - 64
tools/pyUANamespace/open62541_MacroHelper.py

@@ -54,17 +54,11 @@ class open62541_MacroHelper():
         returns: C-printable string representation of input
     '''
     # No punctuation characters <>!$
-    illegal_chars = list(string.punctuation)
-    # underscore is allowed
-    illegal_chars.remove('_')
-
-    illegal = "".join(illegal_chars)
-    substitution = ""
-    # Map all punctuation characters to underscore
-    for illegal_char in illegal_chars:
-        substitution = substitution + '_'
-
-    return input.translate(string.maketrans(illegal, substitution), illegal)
+    for illegal_char in list(string.punctuation):
+        if illegal_char == '_': # underscore is allowed
+            continue
+        input = input.replace(illegal_char, "_") # map to underscore
+    return input
 
   def getNodeIdDefineString(self, node):
     code = []
@@ -147,57 +141,58 @@ class open62541_MacroHelper():
 
     # Node ordering should have made sure that arguments, if they exist, have not been printed yet
     if node.nodeClass() == NODE_CLASS_METHOD:
-      inArgVal = []
-      outArgVal = []
-      code.append("UA_Argument *inputArguments = NULL;")
-      code.append("UA_Argument *outputArguments = NULL;")
-      for r in node.getReferences():
-	if r.target() != None and r.target().nodeClass() == NODE_CLASS_VARIABLE and r.target().browseName() == 'InputArguments':
-          while r.target() in unprintedNodes:
-            unprintedNodes.remove(r.target())
-	  if r.target().value() != None:
-	    inArgVal = r.target().value().value
-	elif r.target() != None and r.target().nodeClass() == NODE_CLASS_VARIABLE and r.target().browseName() == 'OutputArguments':
-	  while r.target() in unprintedNodes:
-            unprintedNodes.remove(r.target())
-	  if r.target().value() != None:
-	    outArgVal = r.target().value().value
-      if len(inArgVal)>0:
-	code.append("")
-	code.append("inputArguments = (UA_Argument *) malloc(sizeof(UA_Argument) * " + str(len(inArgVal)) + ");")
-	code.append("int inputArgumentCnt;")
-	code.append("for (inputArgumentCnt=0; inputArgumentCnt<" + str(len(inArgVal)) + "; inputArgumentCnt++) UA_Argument_init(&inputArguments[inputArgumentCnt]); ")
-	argumentCnt = 0
-	for inArg in inArgVal:
-	  if inArg.getValueFieldByAlias("Description") != None:
-	    code.append("inputArguments[" + str(argumentCnt) + "].description = UA_LOCALIZEDTEXT(\"" + str(inArg.getValueFieldByAlias("Description")[0]) + "\",\"" + str(inArg.getValueFieldByAlias("Description")[1]) + "\");")
-	  if inArg.getValueFieldByAlias("Name") != None:
-	    code.append("inputArguments[" + str(argumentCnt) + "].name = UA_STRING(\"" + str(inArg.getValueFieldByAlias("Name")) + "\");")
-	  if inArg.getValueFieldByAlias("ValueRank") != None:
-	    code.append("inputArguments[" + str(argumentCnt) + "].valueRank = " + str(inArg.getValueFieldByAlias("ValueRank")) + ";")
-	  if inArg.getValueFieldByAlias("DataType") != None:
-	    code.append("inputArguments[" + str(argumentCnt) + "].dataType = " + str(self.getCreateNodeIDMacro(inArg.getValueFieldByAlias("DataType"))) + ";")
-	  #if inArg.getValueFieldByAlias("ArrayDimensions") != None:
-	  #  code.append("inputArguments[" + str(argumentCnt) + "].arrayDimensions = " + str(inArg.getValueFieldByAlias("ArrayDimensions")) + ";")
-	  argumentCnt += 1
-      if len(outArgVal)>0:
-	code.append("")
-	code.append("outputArguments = (UA_Argument *) malloc(sizeof(UA_Argument) * " + str(len(outArgVal)) + ");")
-	code.append("int outputArgumentCnt;")
-	code.append("for (outputArgumentCnt=0; outputArgumentCnt<" + str(len(outArgVal)) + "; outputArgumentCnt++) UA_Argument_init(&outputArguments[outputArgumentCnt]); ")
-	argumentCnt = 0
-	for outArg in outArgVal:
-	  if outArg.getValueFieldByAlias("Description") != None:
-	    code.append("outputArguments[" + str(argumentCnt) + "].description = UA_LOCALIZEDTEXT(\"" + str(outArg.getValueFieldByAlias("Description")[0]) + "\",\"" + str(outArg.getValueFieldByAlias("Description")[1]) + "\");")
-	  if outArg.getValueFieldByAlias("Name") != None:
-	    code.append("outputArguments[" + str(argumentCnt) + "].name = UA_STRING(\"" + str(outArg.getValueFieldByAlias("Name")) + "\");")
-	  if outArg.getValueFieldByAlias("ValueRank") != None:
-	    code.append("outputArguments[" + str(argumentCnt) + "].valueRank = " + str(outArg.getValueFieldByAlias("ValueRank")) + ";")
-	  if outArg.getValueFieldByAlias("DataType") != None:
-	    code.append("outputArguments[" + str(argumentCnt) + "].dataType = " + str(self.getCreateNodeIDMacro(outArg.getValueFieldByAlias("DataType"))) + ";")
-	  #if outArg.getValueFieldByAlias("ArrayDimensions") != None:
-	  #  code.append("outputArguments[" + str(argumentCnt) + "].arrayDimensions = " + str(outArg.getValueFieldByAlias("ArrayDimensions")) + ";")
-	  argumentCnt += 1
+        inArgVal = []
+        outArgVal = []
+        code.append("UA_Argument *inputArguments = NULL;")
+        code.append("UA_Argument *outputArguments = NULL;")
+        for r in node.getReferences():
+            if r.isForward():
+                if r.target() != None and r.target().nodeClass() == NODE_CLASS_VARIABLE and r.target().browseName() == 'InputArguments':
+                    while r.target() in unprintedNodes:
+                        unprintedNodes.remove(r.target())
+                    if r.target().value() != None:
+                        inArgVal = r.target().value().value
+                elif r.target() != None and r.target().nodeClass() == NODE_CLASS_VARIABLE and r.target().browseName() == 'OutputArguments':
+                    while r.target() in unprintedNodes:
+                        unprintedNodes.remove(r.target())
+                    if r.target().value() != None:
+                        outArgVal = r.target().value().value
+        if len(inArgVal)>0:
+            code.append("")
+            code.append("inputArguments = (UA_Argument *) malloc(sizeof(UA_Argument) * " + str(len(inArgVal)) + ");")
+            code.append("int inputArgumentCnt;")
+            code.append("for (inputArgumentCnt=0; inputArgumentCnt<" + str(len(inArgVal)) + "; inputArgumentCnt++) UA_Argument_init(&inputArguments[inputArgumentCnt]); ")
+            argumentCnt = 0
+            for inArg in inArgVal:
+                if inArg.getValueFieldByAlias("Description") != None:
+                    code.append("inputArguments[" + str(argumentCnt) + "].description = UA_LOCALIZEDTEXT(\"" + str(inArg.getValueFieldByAlias("Description")[0]) + "\",\"" + str(inArg.getValueFieldByAlias("Description")[1]) + "\");")
+                if inArg.getValueFieldByAlias("Name") != None:
+                    code.append("inputArguments[" + str(argumentCnt) + "].name = UA_STRING(\"" + str(inArg.getValueFieldByAlias("Name")) + "\");")
+                if inArg.getValueFieldByAlias("ValueRank") != None:
+                    code.append("inputArguments[" + str(argumentCnt) + "].valueRank = " + str(inArg.getValueFieldByAlias("ValueRank")) + ";")
+                if inArg.getValueFieldByAlias("DataType") != None:
+                    code.append("inputArguments[" + str(argumentCnt) + "].dataType = " + str(self.getCreateNodeIDMacro(inArg.getValueFieldByAlias("DataType"))) + ";")
+                #if inArg.getValueFieldByAlias("ArrayDimensions") != None:
+                #  code.append("inputArguments[" + str(argumentCnt) + "].arrayDimensions = " + str(inArg.getValueFieldByAlias("ArrayDimensions")) + ";")
+                argumentCnt += 1
+        if len(outArgVal)>0:
+            code.append("")
+            code.append("outputArguments = (UA_Argument *) malloc(sizeof(UA_Argument) * " + str(len(outArgVal)) + ");")
+            code.append("int outputArgumentCnt;")
+            code.append("for (outputArgumentCnt=0; outputArgumentCnt<" + str(len(outArgVal)) + "; outputArgumentCnt++) UA_Argument_init(&outputArguments[outputArgumentCnt]); ")
+            argumentCnt = 0
+            for outArg in outArgVal:
+                if outArg.getValueFieldByAlias("Description") != None:
+                    code.append("outputArguments[" + str(argumentCnt) + "].description = UA_LOCALIZEDTEXT(\"" + str(outArg.getValueFieldByAlias("Description")[0]) + "\",\"" + str(outArg.getValueFieldByAlias("Description")[1]) + "\");")
+                if outArg.getValueFieldByAlias("Name") != None:
+                    code.append("outputArguments[" + str(argumentCnt) + "].name = UA_STRING(\"" + str(outArg.getValueFieldByAlias("Name")) + "\");")
+                if outArg.getValueFieldByAlias("ValueRank") != None:
+                    code.append("outputArguments[" + str(argumentCnt) + "].valueRank = " + str(outArg.getValueFieldByAlias("ValueRank")) + ";")
+                if outArg.getValueFieldByAlias("DataType") != None:
+                    code.append("outputArguments[" + str(argumentCnt) + "].dataType = " + str(self.getCreateNodeIDMacro(outArg.getValueFieldByAlias("DataType"))) + ";")
+                #if outArg.getValueFieldByAlias("ArrayDimensions") != None:
+                #  code.append("outputArguments[" + str(argumentCnt) + "].arrayDimensions = " + str(outArg.getValueFieldByAlias("ArrayDimensions")) + ";")
+                argumentCnt += 1
 
     # print the attributes struct
     code.append("UA_%sAttributes attr;" % nodetype)
@@ -209,9 +204,9 @@ class open62541_MacroHelper():
       code = code + node.printOpen62541CCode_SubtypeEarly(bootstrapping = False)
     elif nodetype == "Method":
       if node.executable():
-	code.append("attr.executable = true;")
+        code.append("attr.executable = true;")
       if node.userExecutable():
-	code.append("attr.userExecutable = true;")
+        code.append("attr.userExecutable = true;")
 
     code.append("UA_NodeId nodeId = " + str(self.getCreateNodeIDMacro(node)) + ";")
     if nodetype in ["Object", "Variable"]:

+ 6 - 6
tools/pyUANamespace/open62541_XMLPreprocessor.py

@@ -146,7 +146,7 @@ class preProcessDocument:
     idDict = {}
 
     for ndid in self.containedNodes:
-      if not idDict.has_key(ndid[0].ns):
+      if not ndid[0].ns in idDict.keys():
         idDict[ndid[0].ns] = 1
       else:
         idDict[ndid[0].ns] = idDict[ndid[0].ns] + 1
@@ -191,7 +191,7 @@ class preProcessDocument:
     outline = self.nodeset.toxml()
     for qualifier in self.namespaceQualifiers:
       rq = qualifier+":"
-      outline = outline.replace(rq.decode('UTF-8'), "")
+      outline = outline.replace(rq, "")
     os.write(outfile, outline.encode('UTF-8'))
     os.close(outfile)
 
@@ -264,7 +264,7 @@ class open62541_XMLPreprocessor:
     for ref in refs:
       for n in doc.containedNodes:
         if str(ref) == str(n[0]):
-          print ref, n[0]
+          print(ref, n[0])
           found = found + 1
           break
     return float(found)/float(sspace)
@@ -348,12 +348,12 @@ class open62541_XMLPreprocessor:
           r.toString()
         # ... how many of them would be found!?
         c = self.testModelCongruencyAgainstReferences(tDoc, refs)
-        print c
+        print(c)
         if c>0:
-          matches.append(c, tDoc)
+          matches.append((c, tDoc))
       best = (0, None)
       for m in matches:
-        print m[0]
+        print(m[0])
         if m[0] > best[0]:
           best = m
       if best[1] != None:

+ 4 - 4
tools/pyUANamespace/ua_builtin_types.py

@@ -65,12 +65,12 @@ class opcua_value_t():
 
   def getValueFieldByAlias(self, fieldname):
     if not isinstance(self.value, list):
-      return None
+        return None
     if not isinstance(self.value[0], opcua_value_t):
-      return None
+        return None
     for val in self.value:
-      if val.alias() == fieldname:
-	return val.value
+        if val.alias() == fieldname:
+            return val.value
     return None
 
   def setEncodingRule(self, encoding):

+ 1 - 1
tools/pyUANamespace/ua_namespace.py

@@ -664,7 +664,7 @@ class opcua_namespace():
       else:
         name =  self.namespaceIdentifiers[nsid]
         name = name.replace("\"","\\\"")
-        code.append("UA_Server_addNamespace(server, \"" + name.encode('UTF-8') + "\");")
+        code.append("UA_Server_addNamespace(server, \"" + name + "\");")
 
     # Find all references necessary to create the namespace and
     # "Bootstrap" them so all other nodes can safely use these referencetypes whenever

+ 2 - 0
tools/pyUANamespace/ua_node_types.py

@@ -219,6 +219,8 @@ class opcua_node_id_t():
     return self.__mystrname__
 
   def __eq__(self, nodeId2):
+    if not nodeId2:
+        return False
     return (self.toString() == nodeId2.toString())
 
   def __repr__(self):