Browse Source

Merge pull request #96 from acplt/automation

Automation branch merge in
Sten 10 years ago
parent
commit
c3bc0c01ae

+ 1 - 1
.travis.yml

@@ -30,7 +30,7 @@ before_install:
 - rm -rf check-code
 script: ./autogen.sh && ./configure --enable-doxygen && make && make check
 after_success:
-   - git clone -b gh-pages https://$GITAUTH@github.com/acplt/open62541
+   - git clone --depth=50 -b gh-pages https://$GITAUTH@github.com/acplt/open62541
    - rm -rf open62541/doxygen
    - cp -r doc/html open62541/doxygen
    - cd open62541/doxygen

+ 13 - 0
configure.ac

@@ -69,6 +69,19 @@ AS_HELP_STRING([--enable-coverage],
 AM_CONDITIONAL(COVERAGE, test x"$coverage" = x"true")
 AM_COND_IF([COVERAGE],
     AC_DEFINE([COVERAGE])) #define MULTITHREADING is accessible from pre-processor
+    
+#raspberry pi
+AC_ARG_ENABLE(raspi,
+AS_HELP_STRING([--enable-raspi],
+               [enable raspi, default: no]),
+	[case "${enableval}" in
+		yes) raspi=true ;;
+		no)  raspi=false ;;
+		*)   AC_MSG_ERROR([bad value ${enableval} for --enable-raspi]) ;; 
+	esac],[raspi=false])
+AM_CONDITIONAL(RASPI, test x"$raspi" = x"true")
+AM_COND_IF([RASPI],
+    AC_DEFINE([RASPI])) #define MULTITHREADING is accessible from pre-processor
 
 #doxygen start
 AC_ARG_ENABLE(doxygen,

+ 4 - 0
examples/src/Makefile.am

@@ -27,6 +27,10 @@ bin_PROGRAMS += $(top_builddir)/bin/exampleServer $(top_builddir)/bin/exampleSer
 __top_builddir__bin_exampleServer_CFLAGS = -I$(top_builddir)/src -I$(top_builddir)/include $(GLOBAL_AM_CFLAGS)
 __top_builddir__bin_exampleServer_SOURCES = opcuaServer.c networklayer.c
 __top_builddir__bin_exampleServer_LDADD= $(top_builddir)/lib/libopen62541.a $(GLOBAL_AM_LDADD)
+if RASPI
+__top_builddir__bin_exampleServer_LDFLAGS = -lwiringPi
+__top_builddir__bin_exampleServer_SOURCES += raspberrypi_io.c
+endif
 
 __top_builddir__bin_exampleServerACPLT_CFLAGS = -I$(top_builddir)/src -I$(top_builddir)/include $(GLOBAL_AM_CFLAGS)
 __top_builddir__bin_exampleServerACPLT_SOURCES = opcuaServerACPLT.c networklayer.c

+ 8 - 2
examples/src/networklayer.c

@@ -16,7 +16,7 @@ NL_Description NL_Description_TcpBinary  = {
 	NL_UA_ENCODING_BINARY,
 	NL_CONNECTIONTYPE_TCPV4,
 	NL_MAXCONNECTIONS_DEFAULT,
-	{-1,8192,8192,16384,1}
+	{0,8192,8192,16384,1}
 };
 
 /* If we do not have multitasking, we implement a dispatcher-Pattern. All Connections
@@ -91,14 +91,18 @@ UA_Int32 NL_msgLoop(NL_data* nl, struct timeval *tv, UA_Int32(*worker)(void*), v
 			default:
 				DBG_VERBOSE(printf("UA_Stack_msgLoop - errno={%d,%s}\n", errno, strerror(errno)));
 				DBG_VERBOSE(printf("UA_Stack_msgLoop - call worker\n"));
-				worker(arg);
+
 				DBG_VERBOSE(printf("UA_Stack_msgLoop - return from worker\n"));
 			}
 		} else { // activity on listener or client ports
 			DBG_VERBOSE(printf("UA_Stack_msgLoop - activities on %d handles\n",result));
 			UA_list_iteratePayload(&(nl->connections),NL_checkFdSet);
+
 		}
+		worker(arg);
+
 	}
+
 	return UA_SUCCESS;
 }
 #endif
@@ -209,7 +213,9 @@ void* NL_Connection_init(NL_Connection* c, NL_data* tld, UA_Int32 connectionHand
 	c->connection.connectionHandle = connectionHandle;
 	c->connection.connectionState = CONNECTIONSTATE_CLOSED;
 	c->connection.writerCallback = writer;
+
 	memcpy(&(c->connection.localConf),&(tld->tld->localConf),sizeof(TL_Buffer));
+
 	memset(&(c->connection.remoteConf),0,sizeof(TL_Buffer));
 	UA_String_copy(&(tld->endpointUrl), &(c->connection.localEndpointUrl));
 

+ 142 - 2
examples/src/opcuaServer.c

@@ -7,6 +7,10 @@
  Description : lala
  ============================================================================
  */
+#ifdef RASPI
+	#define _POSIX_C_SOURCE 199309L //to use nanosleep
+	#include <pthread.h>
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -14,16 +18,152 @@
 #include "networklayer.h"
 #include "ua_application.h"
 
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#ifdef RASPI
+	#include "raspberrypi_io.h"
+#endif
+
+
 UA_Int32 serverCallback(void * arg) {
 	char *name = (char *) arg;
 	printf("%s does whatever servers do\n",name);
+
+	Namespace* ns0 = (Namespace*)UA_indexedList_find(appMockup.namespaces, 0)->payload;
+	UA_Int32 retval;
+	UA_Node const * node;
+	UA_ExpandedNodeId serverStatusNodeId = NS0EXPANDEDNODEID(2256);
+	retval = Namespace_get(ns0, &(serverStatusNodeId.nodeId),&node, UA_NULL);
+	if(retval == UA_SUCCESS){
+		((UA_ServerStatusDataType*)(((UA_VariableNode*)node)->value.data))->currentTime = UA_DateTime_now();
+	}
+
+	const UA_Node *foundNode1 = UA_NULL;
+
+	Namespace_Entry_Lock *lock;
+	//node which should be filled with data (float value)
+	UA_NodeId tmpNodeId1;
+
+
+	tmpNodeId1.encodingByte = UA_NODEIDTYPE_TWOBYTE;
+	tmpNodeId1.identifier.numeric = 108;
+	tmpNodeId1.namespace =  0;
+
+
+	if(Namespace_get(ns0,&tmpNodeId1, &foundNode1,&lock) != UA_SUCCESS){
+		return UA_ERROR;
+	}
+
+
+#ifdef RASPI
+#else
+	*((float*)((UA_VariableNode *)foundNode1)->value.data) = *((float*)((UA_VariableNode *)foundNode1)->value.data) + 0.2f;
+#endif
+
+
+
 	return UA_SUCCESS;
 }
 
+#ifdef RASPI
+
+static float sharedTemperature = 0;
+static UA_Boolean sharedLED1 = 0;
+static UA_Boolean sharedLED2 = 0;
+
+static void* ioloop(void* ptr){
+    {
+	if(initIO())
+	{
+		printf("ERROR while initializing the IO \n");
+	}
+	else
+	{
+	        struct timespec t = {0, 50*1000*1000};
+		int j = 0;
+		for(;j<25;j++){
+		writePin(j%2,0);
+		writePin((j+1)%2,2);
+		nanosleep(&t, NULL);
+		}
+		writePin(0,0);
+		writePin(0,2);
+
+		printf("IO successfully initalized \n");
+	}
+
+	printf("done - io init \n");
+
+  
+
+        struct timespec t = {0, 50*1000*1000};
+    	while(1){
+		readTemp(&sharedTemperature);
+		writePin(sharedLED1,0);
+		writePin(sharedLED2,2);
+		nanosleep(&t, NULL);
+    	}
+    }
+	return UA_NULL;
+}
+#endif
+
 int main(int argc, char** argv) {
 	appMockup_init();
 	NL_data* nl = NL_init(&NL_Description_TcpBinary,16664);
 
-	struct timeval tv = {20, 0}; // 20 seconds
-	NL_msgLoop(nl, &tv,serverCallback,argv[0]);
+	struct timeval tv = {1, 0}; // 1 second
+#ifdef RASPI
+  	Namespace* ns0 = (Namespace*)UA_indexedList_find(appMockup.namespaces, 0)->payload;
+		const UA_Node *foundNode1 = UA_NULL;
+		const UA_Node *foundNode2 = UA_NULL;
+		const UA_Node *foundNode3 = UA_NULL;
+		Namespace_Entry_Lock *lock;
+		//node which should be filled with data (float value)
+		UA_NodeId tmpNodeId1;
+		UA_NodeId tmpNodeId2;
+		UA_NodeId tmpNodeId3;
+
+		tmpNodeId1.encodingByte = UA_NODEIDTYPE_TWOBYTE;
+		tmpNodeId1.identifier.numeric = 108;
+		tmpNodeId1.namespace =  0;
+
+		tmpNodeId2.encodingByte = UA_NODEIDTYPE_TWOBYTE;
+		tmpNodeId2.identifier.numeric = 109;
+		tmpNodeId2.namespace =  0;
+
+		tmpNodeId3.encodingByte = UA_NODEIDTYPE_TWOBYTE;
+		tmpNodeId3.identifier.numeric = 110;
+		tmpNodeId3.namespace =  0;
+            	
+			if(Namespace_get(ns0,&tmpNodeId1, &foundNode1,&lock) != UA_SUCCESS){
+				_exit(1);
+			}
+
+			if(Namespace_get(ns0,&tmpNodeId2, &foundNode2,&lock) != UA_SUCCESS){
+				_exit(1);
+			}
+
+			if(Namespace_get(ns0,&tmpNodeId3, &foundNode3,&lock) != UA_SUCCESS){
+				_exit(1);
+			}
+
+		((UA_VariableNode *)foundNode1)->value.data = &sharedTemperature;
+		((UA_VariableNode *)foundNode2)->value.data = &sharedLED1;
+		((UA_VariableNode *)foundNode3)->value.data = &sharedLED2;
+
+		pthread_t t;
+		pthread_create(&t, NULL, &ioloop, UA_NULL);
+
+	printf("raspi enabled \n");	
+  	//pid_t i = fork();
+	//printf("process id i=%d \n",i);
+#endif    
+
+  	NL_msgLoop(nl, &tv, serverCallback, argv[0]);
+
 }

+ 65 - 0
examples/src/raspberrypi_io.c

@@ -0,0 +1,65 @@
+/*
+ * raspberrypi_io.c
+ *
+ *  Created on: 23.06.2014
+ *      Author: root
+ */
+
+#include "raspberrypi_io.h"
+
+
+
+#ifdef RASPI
+static _Bool initialized = 0;
+int initIO()
+{
+	if (wiringPiSetup() == -1){
+		initialized = 0;
+		return 1;
+	}
+	initialized = 1;
+	return 0;
+}
+int readTemp(float *temp){
+	FILE *ptr_file;
+	char buf[1000];
+	char delimiter[] = "=";
+	char *ptr_temp;
+	long temperaturInmC;
+
+	ptr_file =fopen("/sys/bus/w1/devices/10-000802c607f3/w1_slave","r");
+	if (!ptr_file){
+		puts("ERROR: no sensor");
+		return 1;
+	}
+
+	fgets(buf, 1000, ptr_file);
+
+	fgets(buf, 1000, ptr_file);
+
+	ptr_temp = strtok(buf, (char*) delimiter);
+
+	ptr_temp = strtok((void*)0, (char*) delimiter);
+
+	temperaturInmC = atol(ptr_temp);
+
+	fclose(ptr_file);
+
+	*temp = (float)temperaturInmC/1000;
+	return 0;
+}
+
+int writePin(_Bool state, int pin){
+	if(initialized)
+	{
+		pinMode(0, OUTPUT);
+		if(state==(1==1)){
+			digitalWrite(pin, 1);
+		}else{
+			digitalWrite(pin, 0);
+		}
+		return 0;
+	}
+	return 1; //ERROR
+}
+#endif

+ 22 - 0
examples/src/raspberrypi_io.h

@@ -0,0 +1,22 @@
+/*
+ * raspberrypi_io.h
+ *
+ *  Created on: 23.06.2014
+ *      Author: root
+ */
+
+#ifndef RASPBERRYPI_IO_H_
+#define RASPBERRYPI_IO_H_
+
+#include <wiringPi.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "string.h"
+int readTemp(float *temp);
+int writePin(_Bool state, int pin);
+int initIO();
+
+#endif /* RASPBERRYPI_IO_H_ */

+ 2 - 0
src/Makefile.am

@@ -24,6 +24,8 @@ libopen62541_la_SOURCES = ua_types.c \
 						  ua_services_securechannel.c \
 						  ua_services_nodemanagement.c \
 						  ua_services_view.c \
+						  ua_services_subscription.c\
+						  ua_services_monitoreditems.c\
 						  ua_application.c \
 						  ua_xml.c
 ua_types.c: ua_namespace_0.h

+ 595 - 21
src/ua_application.c

@@ -1,5 +1,6 @@
 #include "ua_application.h"
 #include "ua_namespace.h"
+#include "ua_services_internal.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -22,7 +23,6 @@ UA_Node* create_node_ns0(UA_Int32 class, UA_Int32 nodeClass, UA_Int32 const id,
 	return n;
 }
 
-#define C2UA_STRING(s) (UA_String) { sizeof(s)-1, (UA_Byte*) s }
 void appMockup_init() {
 	// create namespaces
 	// TODO: A table that maps the namespaceUris to Ids
@@ -37,26 +37,600 @@ void appMockup_init() {
 	UA_indexedList_addValueToFront(appMockup.namespaces,0,ns0);
 	UA_indexedList_addValueToFront(appMockup.namespaces,1,local);
 
-	UA_Node* np;
-	np = create_node_ns0(UA_OBJECTNODE, UA_NODECLASS_OBJECT, 2253, "Server", "open62541", "...");
-	Namespace_insert(ns0,np);
-	UA_ObjectNode* o = (UA_ObjectNode*)np;
-	o->eventNotifier = UA_FALSE;
-
-	np = create_node_ns0(UA_VARIABLENODE, UA_NODECLASS_VARIABLE, 2255, "Server_NamespaceArray", "open62541", "..." );
-	UA_VariableNode* v = (UA_VariableNode*)np;
-	UA_Array_new((void**)&v->value.data, 2, &UA_.types[UA_STRING]);
-	v->value.vt = &UA_.types[UA_STRING];
-	v->value.arrayLength = 2;
-	UA_String_copycstring("http://opcfoundation.org/UA/",&((UA_String *)((v->value).data))[0]);
-	UA_String_copycstring("http://localhost:16664/open62541/",&((UA_String *)(((v)->value).data))[1]);
-	v->dataType.encodingByte = UA_NODEIDTYPE_FOURBYTE;
-	v->dataType.identifier.numeric = UA_STRING_NS0;
-	v->valueRank = 1;
-	v->minimumSamplingInterval = 1.0;
-	v->historizing = UA_FALSE;
-
-	Namespace_insert(ns0,np);
+	/**************/
+	/* References */
+	/**************/
+
+	// ReferenceType Ids
+	UA_NodeId RefTypeId_References = NS0NODEID(31);
+	UA_NodeId RefTypeId_NonHierarchicalReferences = NS0NODEID(32);
+	UA_NodeId RefTypeId_HierarchicalReferences = NS0NODEID(33);
+	UA_NodeId RefTypeId_HasChild = NS0NODEID(34);
+	UA_NodeId RefTypeId_Organizes = NS0NODEID(35);
+	UA_NodeId RefTypeId_HasEventSource = NS0NODEID(36);
+	UA_NodeId RefTypeId_HasModellingRule = NS0NODEID(37);
+	UA_NodeId RefTypeId_HasEncoding = NS0NODEID(38);
+	UA_NodeId RefTypeId_HasDescription = NS0NODEID(39);
+	UA_NodeId RefTypeId_HasTypeDefinition = NS0NODEID(40);
+	UA_NodeId RefTypeId_GeneratesEvent = NS0NODEID(41);
+	UA_NodeId RefTypeId_Aggregates = NS0NODEID(44);
+	UA_NodeId RefTypeId_HasSubtype = NS0NODEID(45);
+	UA_NodeId RefTypeId_HasProperty = NS0NODEID(46);
+	UA_NodeId RefTypeId_HasComponent = NS0NODEID(47);
+	UA_NodeId RefTypeId_HasNotifier = NS0NODEID(48);
+	UA_NodeId RefTypeId_HasOrderedComponent = NS0NODEID(49);
+	UA_NodeId RefTypeId_HasModelParent = NS0NODEID(50);
+	UA_NodeId RefTypeId_FromState = NS0NODEID(51);
+	UA_NodeId RefTypeId_ToState = NS0NODEID(52);
+	UA_NodeId RefTypeId_HasCause = NS0NODEID(53);
+	UA_NodeId RefTypeId_HasEffect = NS0NODEID(54);
+	UA_NodeId RefTypeId_HasHistoricalConfiguration = NS0NODEID(56);
+
+	UA_ReferenceTypeNode *references;
+	UA_ReferenceTypeNode_new(&references);
+	references->nodeId = RefTypeId_References;
+	references->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	references->browseName = UA_QUALIFIEDNAME_STATIC("References");
+	references->displayName = UA_LOCALIZEDTEXT_STATIC("References");
+	references->description = UA_LOCALIZEDTEXT_STATIC("References");
+	references->isAbstract = UA_TRUE;
+	references->symmetric = UA_TRUE;
+	Namespace_insert(ns0,(UA_Node*)references);
+
+	UA_ReferenceTypeNode *hierarchicalreferences;
+	UA_ReferenceTypeNode_new(&hierarchicalreferences);
+	hierarchicalreferences->nodeId = RefTypeId_HierarchicalReferences;
+	hierarchicalreferences->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hierarchicalreferences->browseName = UA_QUALIFIEDNAME_STATIC("HierarchicalReferences");
+	hierarchicalreferences->displayName = UA_LOCALIZEDTEXT_STATIC("HierarchicalReferences");
+	hierarchicalreferences->description = UA_LOCALIZEDTEXT_STATIC("HierarchicalReferences");
+	hierarchicalreferences->isAbstract = UA_TRUE;
+	hierarchicalreferences->symmetric = UA_FALSE;
+	AddReference((UA_Node*)hierarchicalreferences, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_References, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hierarchicalreferences);
+
+	UA_ReferenceTypeNode *nonhierarchicalreferences;
+	UA_ReferenceTypeNode_new(&nonhierarchicalreferences);
+	nonhierarchicalreferences->nodeId = RefTypeId_NonHierarchicalReferences;
+	nonhierarchicalreferences->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	nonhierarchicalreferences->browseName = UA_QUALIFIEDNAME_STATIC("NonHierarchicalReferences");
+	nonhierarchicalreferences->displayName = UA_LOCALIZEDTEXT_STATIC("NonHierarchicalReferences");
+	nonhierarchicalreferences->description = UA_LOCALIZEDTEXT_STATIC("NonHierarchicalReferences");
+	nonhierarchicalreferences->isAbstract = UA_TRUE;
+	nonhierarchicalreferences->symmetric = UA_FALSE;
+	AddReference((UA_Node*)nonhierarchicalreferences, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_References, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)nonhierarchicalreferences);
+
+	UA_ReferenceTypeNode *haschild;
+	UA_ReferenceTypeNode_new(&haschild);
+	haschild->nodeId = RefTypeId_HasChild;
+	haschild->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	haschild->browseName = UA_QUALIFIEDNAME_STATIC("HasChild");
+	haschild->displayName = UA_LOCALIZEDTEXT_STATIC("HasChild");
+	haschild->description = UA_LOCALIZEDTEXT_STATIC("HasChild");
+	haschild->isAbstract = UA_TRUE;
+	haschild->symmetric = UA_FALSE;
+	AddReference((UA_Node*)haschild, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_HierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)haschild);
+
+	UA_ReferenceTypeNode *organizes;
+	UA_ReferenceTypeNode_new(&organizes);
+	organizes->nodeId = RefTypeId_Organizes;
+	organizes->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	organizes->browseName = UA_QUALIFIEDNAME_STATIC("Organizes");
+	organizes->displayName = UA_LOCALIZEDTEXT_STATIC("Organizes");
+	organizes->description = UA_LOCALIZEDTEXT_STATIC("Organizes");
+	organizes->isAbstract = UA_FALSE;
+	organizes->symmetric = UA_FALSE;
+	organizes->inverseName = UA_LOCALIZEDTEXT_STATIC("OrganizedBy");
+	AddReference((UA_Node*)organizes, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_HierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)organizes);
+
+	UA_ReferenceTypeNode *haseventsource;
+	UA_ReferenceTypeNode_new(&haseventsource);
+	haseventsource->nodeId = RefTypeId_HasEventSource;
+	haseventsource->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	haseventsource->browseName = UA_QUALIFIEDNAME_STATIC("HasEventSource");
+	haseventsource->displayName = UA_LOCALIZEDTEXT_STATIC("HasEventSource");
+	haseventsource->description = UA_LOCALIZEDTEXT_STATIC("HasEventSource");
+	haseventsource->isAbstract = UA_FALSE;
+	haseventsource->symmetric = UA_FALSE;
+	haseventsource->inverseName = UA_LOCALIZEDTEXT_STATIC("EventSourceOf");
+	AddReference((UA_Node*)haseventsource, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_HierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)haseventsource);
+
+	UA_ReferenceTypeNode *hasmodellingrule;
+	UA_ReferenceTypeNode_new(&hasmodellingrule);
+	hasmodellingrule->nodeId = RefTypeId_HasModellingRule;
+	hasmodellingrule->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hasmodellingrule->browseName = UA_QUALIFIEDNAME_STATIC("HasModellingRule");
+	hasmodellingrule->displayName = UA_LOCALIZEDTEXT_STATIC("HasModellingRule");
+	hasmodellingrule->description = UA_LOCALIZEDTEXT_STATIC("HasModellingRule");
+	hasmodellingrule->isAbstract = UA_FALSE;
+	hasmodellingrule->symmetric = UA_FALSE;
+	hasmodellingrule->inverseName = UA_LOCALIZEDTEXT_STATIC("ModellingRuleOf");
+	AddReference((UA_Node*)hasmodellingrule, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hasmodellingrule);
+
+	UA_ReferenceTypeNode *hasencoding;
+	UA_ReferenceTypeNode_new(&hasencoding);
+	hasencoding->nodeId = RefTypeId_HasEncoding;
+	hasencoding->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hasencoding->browseName = UA_QUALIFIEDNAME_STATIC("HasEncoding");
+	hasencoding->displayName = UA_LOCALIZEDTEXT_STATIC("HasEncoding");
+	hasencoding->description = UA_LOCALIZEDTEXT_STATIC("HasEncoding");
+	hasencoding->isAbstract = UA_FALSE;
+	hasencoding->symmetric = UA_FALSE;
+	hasencoding->inverseName = UA_LOCALIZEDTEXT_STATIC("EncodingOf");
+	AddReference((UA_Node*)hasencoding, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hasencoding);
+
+	UA_ReferenceTypeNode *hasdescription;
+	UA_ReferenceTypeNode_new(&hasdescription);
+	hasdescription->nodeId = RefTypeId_HasDescription;
+	hasdescription->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hasdescription->browseName = UA_QUALIFIEDNAME_STATIC("HasDescription");
+	hasdescription->displayName = UA_LOCALIZEDTEXT_STATIC("HasDescription");
+	hasdescription->description = UA_LOCALIZEDTEXT_STATIC("HasDescription");
+	hasdescription->isAbstract = UA_FALSE;
+	hasdescription->symmetric = UA_FALSE;
+	hasdescription->inverseName = UA_LOCALIZEDTEXT_STATIC("DescriptionOf");
+	AddReference((UA_Node*)hasdescription, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hasdescription);
+
+	UA_ReferenceTypeNode *hastypedefinition;
+	UA_ReferenceTypeNode_new(&hastypedefinition);
+	hastypedefinition->nodeId = RefTypeId_HasTypeDefinition;
+	hastypedefinition->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hastypedefinition->browseName = UA_QUALIFIEDNAME_STATIC("HasTypeDefinition");
+	hastypedefinition->displayName = UA_LOCALIZEDTEXT_STATIC("HasTypeDefinition");
+	hastypedefinition->description = UA_LOCALIZEDTEXT_STATIC("HasTypeDefinition");
+	hastypedefinition->isAbstract = UA_FALSE;
+	hastypedefinition->symmetric = UA_FALSE;
+	hastypedefinition->inverseName = UA_LOCALIZEDTEXT_STATIC("TypeDefinitionOf");
+	AddReference((UA_Node*)hastypedefinition, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hastypedefinition);
+
+	UA_ReferenceTypeNode *generatesevent;
+	UA_ReferenceTypeNode_new(&generatesevent);
+	generatesevent->nodeId = RefTypeId_GeneratesEvent;
+	generatesevent->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	generatesevent->browseName = UA_QUALIFIEDNAME_STATIC("GeneratesEvent");
+	generatesevent->displayName = UA_LOCALIZEDTEXT_STATIC("GeneratesEvent");
+	generatesevent->description = UA_LOCALIZEDTEXT_STATIC("GeneratesEvent");
+	generatesevent->isAbstract = UA_FALSE;
+	generatesevent->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("GeneratedBy");
+	AddReference((UA_Node*)generatesevent, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)generatesevent);
+
+	UA_ReferenceTypeNode *aggregates;
+	UA_ReferenceTypeNode_new(&aggregates);
+	aggregates->nodeId = RefTypeId_Aggregates;
+	aggregates->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	aggregates->browseName = UA_QUALIFIEDNAME_STATIC("Aggregates");
+	aggregates->displayName = UA_LOCALIZEDTEXT_STATIC("Aggregates");
+	aggregates->description = UA_LOCALIZEDTEXT_STATIC("Aggregates");
+	aggregates->isAbstract = UA_TRUE;
+	aggregates->symmetric = UA_FALSE;
+	AddReference((UA_Node*)aggregates, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_HasChild, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)aggregates);
+
+	UA_ReferenceTypeNode *hassubtype;
+	UA_ReferenceTypeNode_new(&hassubtype);
+	hassubtype->nodeId = RefTypeId_HasSubtype;
+	hassubtype->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hassubtype->browseName = UA_QUALIFIEDNAME_STATIC("HasSubtype");
+	hassubtype->displayName = UA_LOCALIZEDTEXT_STATIC("HasSubtype");
+	hassubtype->description = UA_LOCALIZEDTEXT_STATIC("HasSubtype");
+	hassubtype->isAbstract = UA_FALSE;
+	hassubtype->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("SubtypeOf");
+	AddReference((UA_Node*)hassubtype, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_HasChild, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hassubtype);
+
+	UA_ReferenceTypeNode *hasproperty;
+	UA_ReferenceTypeNode_new(&hasproperty);
+	hasproperty->nodeId = RefTypeId_HasProperty;
+	hasproperty->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hasproperty->browseName = UA_QUALIFIEDNAME_STATIC("HasProperty");
+	hasproperty->displayName = UA_LOCALIZEDTEXT_STATIC("HasProperty");
+	hasproperty->description = UA_LOCALIZEDTEXT_STATIC("HasProperty");
+	hasproperty->isAbstract = UA_FALSE;
+	hasproperty->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("PropertyOf");
+	AddReference((UA_Node*)hasproperty, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_Aggregates, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hasproperty);
+
+	UA_ReferenceTypeNode *hascomponent;
+	UA_ReferenceTypeNode_new(&hascomponent);
+	hascomponent->nodeId = RefTypeId_HasComponent;
+	hascomponent->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hascomponent->browseName = UA_QUALIFIEDNAME_STATIC("HasComponent");
+	hascomponent->displayName = UA_LOCALIZEDTEXT_STATIC("HasComponent");
+	hascomponent->description = UA_LOCALIZEDTEXT_STATIC("HasComponent");
+	hascomponent->isAbstract = UA_FALSE;
+	hascomponent->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("ComponentOf");
+	AddReference((UA_Node*)hascomponent, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_Aggregates, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hascomponent);
+
+	UA_ReferenceTypeNode *hasnotifier;
+	UA_ReferenceTypeNode_new(&hasnotifier);
+	hasnotifier->nodeId = RefTypeId_HasNotifier;
+	hasnotifier->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hasnotifier->browseName = UA_QUALIFIEDNAME_STATIC("HasNotifier");
+	hasnotifier->displayName = UA_LOCALIZEDTEXT_STATIC("HasNotifier");
+	hasnotifier->description = UA_LOCALIZEDTEXT_STATIC("HasNotifier");
+	hasnotifier->isAbstract = UA_FALSE;
+	hasnotifier->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("NotifierOf");
+	AddReference((UA_Node*)hasnotifier, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_HasEventSource, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hasnotifier);
+
+	UA_ReferenceTypeNode *hasorderedcomponent;
+	UA_ReferenceTypeNode_new(&hasorderedcomponent);
+	hasorderedcomponent->nodeId = RefTypeId_HasOrderedComponent;
+	hasorderedcomponent->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hasorderedcomponent->browseName = UA_QUALIFIEDNAME_STATIC("HasOrderedComponent");
+	hasorderedcomponent->displayName = UA_LOCALIZEDTEXT_STATIC("HasOrderedComponent");
+	hasorderedcomponent->description = UA_LOCALIZEDTEXT_STATIC("HasOrderedComponent");
+	hasorderedcomponent->isAbstract = UA_FALSE;
+	hasorderedcomponent->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("OrderedComponentOf");
+	AddReference((UA_Node*)hasorderedcomponent, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_HasComponent, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hasorderedcomponent);
+
+	UA_ReferenceTypeNode *hasmodelparent;
+	UA_ReferenceTypeNode_new(&hasmodelparent);
+	hasmodelparent->nodeId = RefTypeId_HasModelParent;
+	hasmodelparent->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hasmodelparent->browseName = UA_QUALIFIEDNAME_STATIC("HasModelParent");
+	hasmodelparent->displayName = UA_LOCALIZEDTEXT_STATIC("HasModelParent");
+	hasmodelparent->description = UA_LOCALIZEDTEXT_STATIC("HasModelParent");
+	hasmodelparent->isAbstract = UA_FALSE;
+	hasmodelparent->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("ModelParentOf");
+	AddReference((UA_Node*)hasmodelparent, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hasmodelparent);
+
+	UA_ReferenceTypeNode *fromstate;
+	UA_ReferenceTypeNode_new(&fromstate);
+	fromstate->nodeId = RefTypeId_FromState;
+	fromstate->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	fromstate->browseName = UA_QUALIFIEDNAME_STATIC("FromState");
+	fromstate->displayName = UA_LOCALIZEDTEXT_STATIC("FromState");
+	fromstate->description = UA_LOCALIZEDTEXT_STATIC("FromState");
+	fromstate->isAbstract = UA_FALSE;
+	fromstate->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("ToTransition");
+	AddReference((UA_Node*)fromstate, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)fromstate);
+
+	UA_ReferenceTypeNode *tostate;
+	UA_ReferenceTypeNode_new(&tostate);
+	tostate->nodeId = RefTypeId_ToState;
+	tostate->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	tostate->browseName = UA_QUALIFIEDNAME_STATIC("ToState");
+	tostate->displayName = UA_LOCALIZEDTEXT_STATIC("ToState");
+	tostate->description = UA_LOCALIZEDTEXT_STATIC("ToState");
+	tostate->isAbstract = UA_FALSE;
+	tostate->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("FromTransition");
+	AddReference((UA_Node*)tostate, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)tostate);
+
+	UA_ReferenceTypeNode *hascause;
+	UA_ReferenceTypeNode_new(&hascause);
+	hascause->nodeId = RefTypeId_HasCause;
+	hascause->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hascause->browseName = UA_QUALIFIEDNAME_STATIC("HasCause");
+	hascause->displayName = UA_LOCALIZEDTEXT_STATIC("HasCause");
+	hascause->description = UA_LOCALIZEDTEXT_STATIC("HasCause");
+	hascause->isAbstract = UA_FALSE;
+	hascause->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("MayBeCausedBy");
+	AddReference((UA_Node*)hascause, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hascause);
+
+	UA_ReferenceTypeNode *haseffect;
+	UA_ReferenceTypeNode_new(&haseffect);
+	haseffect->nodeId = RefTypeId_HasEffect;
+	haseffect->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	haseffect->browseName = UA_QUALIFIEDNAME_STATIC("HasEffect");
+	haseffect->displayName = UA_LOCALIZEDTEXT_STATIC("HasEffect");
+	haseffect->description = UA_LOCALIZEDTEXT_STATIC("HasEffect");
+	haseffect->isAbstract = UA_FALSE;
+	haseffect->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("MayBeEffectedBy");
+	AddReference((UA_Node*)haseffect, &(UA_ReferenceNode){RefTypeId_HasSubtype, UA_TRUE,
+		(UA_ExpandedNodeId){RefTypeId_NonHierarchicalReferences, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)haseffect);
+
+	UA_ReferenceTypeNode *hashistoricalconfiguration;
+	UA_ReferenceTypeNode_new(&hashistoricalconfiguration);
+	hashistoricalconfiguration->nodeId = RefTypeId_HasHistoricalConfiguration;
+	hashistoricalconfiguration->nodeClass = UA_NODECLASS_REFERENCETYPE;
+	hashistoricalconfiguration->browseName = UA_QUALIFIEDNAME_STATIC("HasHistoricalConfiguration");
+	hashistoricalconfiguration->displayName = UA_LOCALIZEDTEXT_STATIC("HasHistoricalConfiguration");
+	hashistoricalconfiguration->description = UA_LOCALIZEDTEXT_STATIC("HasHistoricalConfiguration");
+	hashistoricalconfiguration->isAbstract = UA_FALSE;
+	hashistoricalconfiguration->symmetric = UA_FALSE;
+	generatesevent->inverseName = UA_LOCALIZEDTEXT_STATIC("HistoricalConfigurationOf");
+	AddReference((UA_Node*)hashistoricalconfiguration, &(UA_ReferenceNode){RefTypeId_HasSubtype,
+		UA_TRUE, (UA_ExpandedNodeId){RefTypeId_Aggregates, UA_STRING_NULL, 0}}, ns0);
+	Namespace_insert(ns0,(UA_Node*)hashistoricalconfiguration);
+
+
+	// ObjectTypes (Ids only)
+	UA_ExpandedNodeId ObjTypeId_FolderType = NS0EXPANDEDNODEID(61);
+
+	// Objects (Ids only)
+	UA_ExpandedNodeId ObjId_ObjectsFolder = NS0EXPANDEDNODEID(85);
+	UA_ExpandedNodeId ObjId_TypesFolder = NS0EXPANDEDNODEID(86);
+	UA_ExpandedNodeId ObjId_ViewsFolder = NS0EXPANDEDNODEID(87);
+	UA_ExpandedNodeId ObjId_Server = NS0EXPANDEDNODEID(2253);
+	UA_ExpandedNodeId ObjId_ServerArray = NS0EXPANDEDNODEID(2254);
+	UA_ExpandedNodeId ObjId_NamespaceArray = NS0EXPANDEDNODEID(2255);
+	UA_ExpandedNodeId ObjId_ServerStatus = NS0EXPANDEDNODEID(2256);
+	UA_ExpandedNodeId ObjId_ServerCapabilities = NS0EXPANDEDNODEID(2268);
+	UA_ExpandedNodeId ObjId_State = NS0EXPANDEDNODEID(2259);
+
+	// FolderType
+	UA_ObjectNode *folderType;
+	UA_ObjectNode_new(&folderType);
+	folderType->nodeId = NS0NODEID(61);
+	folderType->nodeClass = UA_NODECLASS_OBJECTTYPE; // I should not have to set this manually
+	folderType->browseName = UA_QUALIFIEDNAME_STATIC("FolderType");
+	folderType->displayName = UA_LOCALIZEDTEXT_STATIC("FolderType");
+	folderType->description = UA_LOCALIZEDTEXT_STATIC("FolderType");
+	Namespace_insert(ns0,(UA_Node*)folderType);
+
+	// Root
+	UA_ObjectNode *root;
+	UA_ObjectNode_new(&root);
+	root->nodeId = NS0NODEID(84);
+	root->nodeClass = UA_NODECLASS_OBJECT; // I should not have to set this manually
+	root->browseName = UA_QUALIFIEDNAME_STATIC("Root");
+	root->displayName = UA_LOCALIZEDTEXT_STATIC("Root");
+	root->description = UA_LOCALIZEDTEXT_STATIC("Root");
+	AddReference((UA_Node*)root, &(UA_ReferenceNode){RefTypeId_HasTypeDefinition, UA_FALSE, ObjTypeId_FolderType}, ns0);
+	AddReference((UA_Node*)root, &(UA_ReferenceNode){RefTypeId_Organizes, UA_FALSE, ObjId_ObjectsFolder}, ns0);
+	AddReference((UA_Node*)root, &(UA_ReferenceNode){RefTypeId_Organizes, UA_FALSE, ObjId_TypesFolder}, ns0);
+	AddReference((UA_Node*)root, &(UA_ReferenceNode){RefTypeId_Organizes, UA_FALSE, ObjId_ViewsFolder}, ns0);
+	Namespace_insert(ns0,(UA_Node*)root);
+
+	// Objects
+	UA_ObjectNode *objects;
+	UA_ObjectNode_new(&objects);
+	objects->nodeId = ObjId_ObjectsFolder.nodeId;
+	objects->nodeClass = UA_NODECLASS_OBJECT;
+	objects->browseName = UA_QUALIFIEDNAME_STATIC("Objects");
+	objects->displayName = UA_LOCALIZEDTEXT_STATIC("Objects");
+	objects->description = UA_LOCALIZEDTEXT_STATIC("Objects");
+	AddReference((UA_Node*)objects, &(UA_ReferenceNode){RefTypeId_HasTypeDefinition, UA_FALSE, ObjTypeId_FolderType}, ns0);
+	AddReference((UA_Node*)objects, &(UA_ReferenceNode){RefTypeId_Organizes, UA_FALSE, ObjId_Server}, ns0);
+	Namespace_insert(ns0,(UA_Node*)objects);
+
+	// Types
+	UA_ObjectNode *types;
+	UA_ObjectNode_new(&types);
+	types->nodeId = ObjId_TypesFolder.nodeId;
+	types->nodeClass = UA_NODECLASS_OBJECT;
+	types->browseName = UA_QUALIFIEDNAME_STATIC("Types");
+	types->displayName = UA_LOCALIZEDTEXT_STATIC("Types");
+	types->description = UA_LOCALIZEDTEXT_STATIC("Types");
+	AddReference((UA_Node*)types, &(UA_ReferenceNode){RefTypeId_HasTypeDefinition, UA_FALSE, ObjTypeId_FolderType}, ns0);
+	Namespace_insert(ns0,(UA_Node*)types);
+
+	// Views
+	UA_ObjectNode *views;
+	UA_ObjectNode_new(&views);
+	views->nodeId = ObjId_ViewsFolder.nodeId;
+	views->nodeClass = UA_NODECLASS_OBJECT;
+	views->browseName = UA_QUALIFIEDNAME_STATIC("Views");
+	views->displayName = UA_LOCALIZEDTEXT_STATIC("Views");
+	views->description = UA_LOCALIZEDTEXT_STATIC("Views");
+	AddReference((UA_Node*)views, &(UA_ReferenceNode){RefTypeId_HasTypeDefinition, UA_FALSE, ObjTypeId_FolderType}, ns0);
+	Namespace_insert(ns0,(UA_Node*)views);
+
+	// Server
+	UA_ObjectNode *server;
+	UA_ObjectNode_new(&server);
+	server->nodeId = ObjId_Server.nodeId;
+	server->nodeClass = UA_NODECLASS_OBJECT;
+	server->browseName = UA_QUALIFIEDNAME_STATIC("Server");
+	server->displayName = UA_LOCALIZEDTEXT_STATIC("Server");
+	server->description = UA_LOCALIZEDTEXT_STATIC("Server");
+	AddReference((UA_Node*)server, &(UA_ReferenceNode){RefTypeId_HasComponent, UA_FALSE, ObjId_ServerCapabilities}, ns0);
+	AddReference((UA_Node*)server, &(UA_ReferenceNode){RefTypeId_HasComponent, UA_FALSE, ObjId_NamespaceArray}, ns0);
+	AddReference((UA_Node*)server, &(UA_ReferenceNode){RefTypeId_HasProperty, UA_FALSE, ObjId_ServerStatus}, ns0);
+	AddReference((UA_Node*)server, &(UA_ReferenceNode){RefTypeId_HasProperty, UA_FALSE, ObjId_ServerArray}, ns0);
+	Namespace_insert(ns0,(UA_Node*)server);
+
+	// NamespaceArray
+	UA_VariableNode *namespaceArray;
+	UA_VariableNode_new(&namespaceArray);
+	namespaceArray->nodeId = ObjId_NamespaceArray.nodeId;
+	namespaceArray->nodeClass = UA_NODECLASS_VARIABLE; //FIXME: this should go into _new?
+	namespaceArray->browseName = UA_QUALIFIEDNAME_STATIC("NamespaceArray");
+	namespaceArray->displayName = UA_LOCALIZEDTEXT_STATIC("NamespaceArray");
+	namespaceArray->description = UA_LOCALIZEDTEXT_STATIC("NamespaceArray");
+	UA_Array_new((void**)&namespaceArray->value.data, 2, &UA_.types[UA_STRING]);
+	namespaceArray->value.vt = &UA_.types[UA_STRING];
+	namespaceArray->value.arrayLength = 2;
+	UA_String_copycstring("http://opcfoundation.org/UA/",&((UA_String *)((namespaceArray->value).data))[0]);
+	UA_String_copycstring("http://localhost:16664/open62541/",&((UA_String *)(((namespaceArray)->value).data))[1]);
+	namespaceArray->arrayDimensionsSize = 1;
+	UA_UInt32* dimensions = UA_NULL;
+	UA_alloc((void**)&dimensions, sizeof(UA_UInt32));
+	*dimensions = 2;
+	namespaceArray->arrayDimensions = dimensions;
+	namespaceArray->dataType = NS0NODEID(UA_STRING_NS0);
+	namespaceArray->valueRank = 1;
+	namespaceArray->minimumSamplingInterval = 1.0;
+	namespaceArray->historizing = UA_FALSE;
+	Namespace_insert(ns0,(UA_Node*)namespaceArray);
+
+	// ServerStatus
+	UA_VariableNode *serverstatus;
+	UA_VariableNode_new(&serverstatus);
+	serverstatus->nodeId = ObjId_ServerStatus.nodeId;
+	serverstatus->nodeClass = UA_NODECLASS_VARIABLE;
+	serverstatus->browseName = UA_QUALIFIEDNAME_STATIC("ServerStatus");
+	serverstatus->displayName = UA_LOCALIZEDTEXT_STATIC("ServerStatus");
+	serverstatus->description = UA_LOCALIZEDTEXT_STATIC("ServerStatus");
+	UA_ServerStatusDataType *status;
+	UA_ServerStatusDataType_new(&status);
+	status->startTime = UA_DateTime_now();
+	status->currentTime = UA_DateTime_now();
+	status->state = UA_SERVERSTATE_RUNNING;
+	status->buildInfo = (UA_BuildInfo){
+		.productUri = UA_STRING_STATIC("open62541.org"),
+				.manufacturerName = UA_STRING_STATIC("open62541"),
+				.productName = UA_STRING_STATIC("open62541"),
+				.softwareVersion = UA_STRING_STATIC("0.0"),
+				.buildNumber = UA_STRING_STATIC("0.0"),
+				.buildDate = UA_DateTime_now()};
+	status->secondsTillShutdown = 99999999;
+	status->shutdownReason = UA_LOCALIZEDTEXT_STATIC("because");
+	serverstatus->value.vt = &UA_.types[UA_SERVERSTATUSDATATYPE]; // gets encoded as an extensionobject
+	serverstatus->value.arrayLength = 1;
+	serverstatus->value.data = status;
+	Namespace_insert(ns0,(UA_Node*)serverstatus);
+
+	// State (Component of ServerStatus)
+	UA_VariableNode *state;
+	UA_VariableNode_new(&state);
+	state->nodeId = ObjId_State.nodeId;
+	state->nodeClass = UA_NODECLASS_VARIABLE;
+	state->browseName = UA_QUALIFIEDNAME_STATIC("State");
+	state->displayName = UA_LOCALIZEDTEXT_STATIC("State");
+	state->description = UA_LOCALIZEDTEXT_STATIC("State");
+	state->value.vt = &UA_borrowed_.types[UA_SERVERSTATE];
+	state->value.arrayLength = 1;
+	state->value.data = &status->state; // points into the other object.
+	Namespace_insert(ns0,(UA_Node*)state);
+
+	//TODO: free(namespaceArray->value.data) later or forget it
+
+
+	/* UA_VariableNode* v = (UA_VariableNode*)np; */
+	/* UA_Array_new((void**)&v->value.data, 2, &UA_.types[UA_STRING]); */
+	/* v->value.vt = &UA_.types[UA_STRING]; */
+	/* v->value.arrayLength = 2; */
+	/* UA_String_copycstring("http://opcfoundation.org/UA/",&((UA_String *)((v->value).data))[0]); */
+	/* UA_String_copycstring("http://localhost:16664/open62541/",&((UA_String *)(((v)->value).data))[1]); */
+	/* v->dataType.encodingByte = UA_NODEIDTYPE_FOURBYTE; */
+	/* v->dataType.identifier.numeric = UA_STRING_NS0; */
+	/* v->valueRank = 1; */
+	/* v->minimumSamplingInterval = 1.0; */
+	/* v->historizing = UA_FALSE; */
+	/* Namespace_insert(ns0,np); */
+
+	/*******************/
+	/* Namespace local */
+	/*******************/
+
+	// WORKS
+	UA_ExpandedNodeId ObjId_temperature1 = (UA_ExpandedNodeId){.nodeId = (UA_NodeId){.encodingByte = UA_NODEIDTYPE_TWOBYTE, .namespace = 0, .identifier.numeric = 108}, .namespaceUri = {-1, ((void *)0)}, .serverIndex = 0};
+
+	// temperature sensor
+	UA_VariableNode *temperature1;
+	UA_VariableNode_new(&temperature1);
+	temperature1->nodeId = ObjId_temperature1.nodeId;
+	temperature1->nodeClass = UA_NODECLASS_VARIABLE;
+	temperature1->browseName = UA_QUALIFIEDNAME_STATIC("temperature1");
+	temperature1->displayName = UA_LOCALIZEDTEXT_STATIC("temperature1");
+	temperature1->description = UA_LOCALIZEDTEXT_STATIC("temperature1");
+
+	//Set node value
+	UA_Variant *tmpNodeValue;
+	UA_Float *tmpFloat;
+	UA_Float_new(&tmpFloat);
+	*tmpFloat = -273.15f;
+	UA_Variant_new(&tmpNodeValue);
+	tmpNodeValue->arrayDimensionsLength = 0;
+	tmpNodeValue->arrayLength = 1;
+	tmpNodeValue->data = (void*)tmpFloat;
+	tmpNodeValue->vt = &UA_.types[UA_FLOAT];
+	UA_Variant_copy(tmpNodeValue, &temperature1->value);
+
+	AddReference((UA_Node*)root, &(UA_ReferenceNode){RefTypeId_Organizes, UA_FALSE, ObjId_temperature1}, ns0);
+
+	Namespace_insert(ns0,(UA_Node*)temperature1);
+
+
+	UA_ExpandedNodeId ObjId_redLED = (UA_ExpandedNodeId){.nodeId = (UA_NodeId){.encodingByte = UA_NODEIDTYPE_TWOBYTE, .namespace = 0, .identifier.numeric = 109}, .namespaceUri = {-1, ((void *)0)}, .serverIndex = 0};
+	// redLED sensor
+	UA_VariableNode *redLED;
+	UA_VariableNode_new(&redLED);
+	redLED->nodeId = ObjId_redLED.nodeId;
+	redLED->nodeClass = UA_NODECLASS_VARIABLE;
+	redLED->browseName = UA_QUALIFIEDNAME_STATIC("redLED");
+	redLED->displayName = UA_LOCALIZEDTEXT_STATIC("redLED");
+	redLED->description = UA_LOCALIZEDTEXT_STATIC("redLED");
+
+	//Set node value
+	UA_Variant *tmpNodeValue1;
+	UA_Boolean *ledVal;
+	UA_Boolean_new(&ledVal);
+	*ledVal = UA_FALSE;
+	UA_Variant_new(&tmpNodeValue1);
+	tmpNodeValue1->arrayDimensionsLength = 0;
+	tmpNodeValue1->arrayLength = 1;
+	tmpNodeValue1->data = (void*)ledVal;
+	tmpNodeValue1->vt = &UA_.types[UA_BOOLEAN];
+	UA_Variant_copy(tmpNodeValue1, &redLED->value);
+
+	AddReference((UA_Node*)root, &(UA_ReferenceNode){RefTypeId_Organizes, UA_FALSE, ObjId_redLED}, ns0);
+	Namespace_insert(ns0,(UA_Node*)redLED);
+
+
+	UA_ExpandedNodeId ObjId_yellowLED = (UA_ExpandedNodeId){.nodeId = (UA_NodeId){.encodingByte = UA_NODEIDTYPE_TWOBYTE, .namespace = 0, .identifier.numeric = 110}, .namespaceUri = {-1, ((void *)0)}, .serverIndex = 0};
+	// yellowLED sensor
+	UA_VariableNode *yellowLED;
+	UA_VariableNode_new(&yellowLED);
+	yellowLED->nodeId = ObjId_yellowLED.nodeId;
+	yellowLED->nodeClass = UA_NODECLASS_VARIABLE;
+	yellowLED->browseName = UA_QUALIFIEDNAME_STATIC("yellowLED");
+	yellowLED->displayName = UA_LOCALIZEDTEXT_STATIC("yellowLED");
+	yellowLED->description = UA_LOCALIZEDTEXT_STATIC("yellowLED");
+
+	//Set node value
+	UA_Variant *tmpNodeValue2;
+	UA_Boolean *ledVal1;
+	UA_Boolean_new(&ledVal1);
+	*ledVal1 = UA_FALSE;
+	UA_Variant_new(&tmpNodeValue2);
+	tmpNodeValue2->arrayDimensionsLength = 0;
+	tmpNodeValue2->arrayLength = 1;
+	tmpNodeValue2->data = (void*)ledVal1;
+	tmpNodeValue2->vt = &UA_.types[UA_BOOLEAN];
+	UA_Variant_copy(tmpNodeValue2, &yellowLED->value);
+
+	AddReference((UA_Node*)root, &(UA_ReferenceNode){RefTypeId_Organizes, UA_FALSE, ObjId_yellowLED}, ns0);
+	Namespace_insert(ns0,(UA_Node*)yellowLED);
 
 #if defined(DEBUG) && defined(VERBOSE)
 	uint32_t i;

+ 19 - 3
src/ua_services.h

@@ -109,7 +109,12 @@ UA_Int32 Service_CloseSession(SL_Channel *channel, const UA_CloseSessionRequest
  * @brief This Service is used to add one or more Nodes into the AddressSpace hierarchy.
  */
 UA_Int32 Service_AddNodes(SL_Channel *channel, const UA_AddNodesRequest *request, UA_AddNodesResponse *response);
-// Service_AddReferences
+
+/**
+ * @brief This Service is used to add one or more References to one or more Nodes
+ */
+UA_Int32 Service_AddReferences(SL_Channel *channel, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response);
+
 // Service_DeleteNodes
 // Service_DeleteReferences
 /** @} */
@@ -178,7 +183,14 @@ UA_Int32 Service_TranslateBrowsePathsToNodeIds(SL_Channel *channel, const UA_Tra
  */
 UA_Int32 Service_Read(SL_Channel *channel, const UA_ReadRequest *request, UA_ReadResponse *response);
 // Service_HistoryRead
-// Service_Write
+/**
+ * @brief This Service is used to write one or more Attributes of one or more
+ *  Nodes. For constructed Attribute values whose elements are indexed, such as
+ *  an array, this Service allows Clients to write the entire set of indexed
+ *  values as a composite, to write individual elements or to write ranges of
+ *  elements of the composite.
+ */
+UA_Int32 Service_Write(SL_Channel *channel, const UA_WriteRequest *request,UA_WriteResponse *response);
 // Service_HistoryUpdate
 /** @} */
 
@@ -225,9 +237,13 @@ UA_Int32 Service_CreateMonitoredItems(SL_Channel *channel, const UA_CreateMonito
  * @{
  */
 // Service_CreateSubscription
+UA_Int32 Service_CreateSubscription(SL_Channel *channel, const UA_CreateSubscriptionRequest *request,
+                                   UA_CreateSubscriptionResponse *response);
 // Service_ModifySubscription
 // Service_SetPublishingMode
-// Service_Publish
+UA_Int32 Service_Publish(SL_Channel *channel, const UA_PublishRequest *request,
+                                   UA_PublishResponse *response);
+
 // Service_Republish
 // Service_TransferSubscription
 // Service_DeleteSubscription

+ 224 - 15
src/ua_services_attribute.c

@@ -43,10 +43,10 @@ static UA_DataValue service_read_node(Application *app, const UA_ReadValueId *id
 	Namespace *ns = UA_indexedList_findValue(app->namespaces, id->nodeId.namespace);
 
 	if(ns == UA_NULL) {
-	DBG_VERBOSE(printf("service_read_node - unknown namespace %d\n", id->nodeId.namespace));
-	v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-	v.status       = UA_STATUSCODE_BADNODEIDUNKNOWN;
-	return v;
+		DBG_VERBOSE(printf("service_read_node - unknown namespace %d\n", id->nodeId.namespace));
+		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+		v.status       = UA_STATUSCODE_BADNODEIDUNKNOWN;
+		return v;
 	}
 
 	UA_Node const *node = UA_NULL;
@@ -55,9 +55,9 @@ static UA_DataValue service_read_node(Application *app, const UA_ReadValueId *id
 	DBG_VERBOSE(UA_NodeId_printf("service_read_node - search for ", &(id->nodeId)));
 	UA_Int32 result = Namespace_get(ns, &(id->nodeId), &node, &lock);
 	if(result != UA_SUCCESS || node == UA_NULL) {
-	v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-	v.status       = UA_STATUSCODE_BADNODEIDUNKNOWN;
-	return v;
+		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+		v.status       = UA_STATUSCODE_BADNODEIDUNKNOWN;
+		return v;
 	}
 	DBG_VERBOSE(UA_NodeId_printf("service_read_node - found node=", &(node->nodeId)));
 
@@ -71,7 +71,7 @@ static UA_DataValue service_read_node(Application *app, const UA_ReadValueId *id
 
 	case UA_ATTRIBUTEID_NODECLASS:
 		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_.types[UA_UINT32], &node->nodeClass);
+		retval |= UA_Variant_copySetValue(&v.value, &UA_.types[UA_INT32], &node->nodeClass);
 		break;
 
 	case UA_ATTRIBUTEID_BROWSENAME:
@@ -86,8 +86,9 @@ static UA_DataValue service_read_node(Application *app, const UA_ReadValueId *id
 		break;
 
 	case UA_ATTRIBUTEID_DESCRIPTION:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-		v.status       = UA_STATUSCODE_BADNOTREADABLE;
+		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+		retval |= UA_Variant_copySetValue(&v.value, &UA_.types[UA_LOCALIZEDTEXT],
+		                                  &node->description);
 		break;
 
 	case UA_ATTRIBUTEID_WRITEMASK:
@@ -140,8 +141,9 @@ static UA_DataValue service_read_node(Application *app, const UA_ReadValueId *id
 		CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
 		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
 		// TODO: Ensure that the borrowed value is not freed prematurely (multithreading)
-		retval |= UA_Variant_borrowSetValue(&v.value, &UA_.types[UA_VARIANT],
-		                                    &((UA_VariableNode *)node)->value);
+		/* retval |= UA_Variant_borrowSetValue(&v.value, &UA_.types[UA_VARIANT], */
+		/*                                     &((UA_VariableNode *)node)->value); */
+		retval |= UA_Variant_copy(&((UA_VariableNode *)node)->value, &v.value);
 		break;
 
 	case UA_ATTRIBUTEID_DATATYPE:
@@ -229,7 +231,7 @@ UA_Int32 Service_Read(SL_Channel *channel, const UA_ReadRequest *request,
 	if(channel->session == UA_NULL || channel->session->application == UA_NULL)
 		return UA_ERROR;    // TODO: Return error message
 
-	int readsize = request->nodesToReadSize;
+	UA_Int32 readsize = request->nodesToReadSize;
 	/* NothingTodo */
 	if(readsize <= 0) {
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
@@ -239,13 +241,220 @@ UA_Int32 Service_Read(SL_Channel *channel, const UA_ReadRequest *request,
 
 	response->resultsSize = readsize;
 	UA_alloc((void **)&response->results, sizeof(UA_DataValue) * readsize);
-	for(int i = 0;i < readsize;i++) {
+	for(UA_Int32 i = 0;i < readsize;i++) {
 		DBG_VERBOSE(printf("service_read - attributeId=%d\n", request->nodesToRead[i].attributeId));
 		DBG_VERBOSE(UA_NodeId_printf("service_read - nodeId=", &(request->nodesToRead[i].nodeId)));
 		response->results[i] = service_read_node(channel->session->application,
 		                                         &request->nodesToRead[i]);
 	}
 	response->responseHeader.serviceResult = UA_STATUSCODE_GOOD;
-	response->diagnosticInfosSize = -1;
+	response->diagnosticInfosSize = 0;
 	return UA_SUCCESS;
 }
+
+UA_Int32 Service_Write_writeNode(Application *app, UA_WriteValue *writeValue, UA_StatusCode *result)
+{
+	UA_Int32 retval = UA_SUCCESS;
+	Namespace *ns = UA_indexedList_findValue(app->namespaces, writeValue->nodeId.namespace);
+	if(ns==UA_NULL)
+	{
+		*result = UA_STATUSCODE_BADNODEIDINVALID;
+		return UA_ERROR;
+	}
+	Namespace_Entry_Lock *lock;
+	const UA_Node *node;
+
+	if(Namespace_get(ns, &writeValue->nodeId,&node, &lock) != UA_SUCCESS){
+		return UA_ERROR;
+	}
+
+
+
+	switch(writeValue->attributeId) {
+	case UA_ATTRIBUTEID_NODEID:
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){
+
+		}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_NODECLASS:
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){
+
+		}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_BROWSENAME:
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_DISPLAYNAME:
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){
+		}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_DESCRIPTION:
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_WRITEMASK:
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		break;
+
+	case UA_ATTRIBUTEID_USERWRITEMASK:
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_ISABSTRACT:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_SYMMETRIC:
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_INVERSENAME:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_EVENTNOTIFIER:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_VALUE:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){
+		// TODO: Ensure that the borrowed value is not freed prematurely (multithreading)
+		/* retval |= UA_Variant_borrowSetValue(&v.value, &UA_.types[UA_VARIANT], */
+		/*                                     &((UA_VariableNode *)node)->value); */
+
+#ifdef RASPI
+		//Sten: this is  highly hacked to cope with pthreads
+		//tested only with raspberrypi and boolean
+		*((UA_Boolean*)((UA_VariableNode *)node)->value.data) = *((UA_Boolean*)(writeValue->value.value.data));
+		//it seems that UA_Variant_copy copies the value out of the shared memory
+#else
+		retval |= UA_Variant_copy(&writeValue->value.value, &((UA_VariableNode *)node)->value);
+#endif
+		*result = UA_STATUSCODE_GOOD;
+		}
+
+		break;
+
+	case UA_ATTRIBUTEID_DATATYPE:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_VALUERANK:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_ACCESSLEVEL:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_USERACCESSLEVEL:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		return UA_ERROR;
+		break;
+
+	case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_HISTORIZING:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_EXECUTABLE:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	case UA_ATTRIBUTEID_USEREXECUTABLE:
+
+		if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){}
+		*result = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+
+		break;
+
+	default:
+
+		*result      = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+		break;
+	}
+
+	Namespace_Entry_Lock_release(lock);
+	return retval;
+
+}
+UA_Int32 Service_Write(SL_Channel *channel, const UA_WriteRequest *request,
+                      UA_WriteResponse *response) {
+	UA_Int32 retval = UA_SUCCESS;
+	UA_Int32 i;
+	if(channel->session == UA_NULL || channel->session->application == UA_NULL)
+		return UA_ERROR;    // TODO: Return error message
+	response->resultsSize = request->nodesToWriteSize;
+	//TODO evalutate diagnostic info within the request
+	UA_Array_new((void**)&response->results,response->resultsSize,&UA_.types[UA_STATUSCODE]);
+	for(i=0; i < request->nodesToWriteSize; i++){
+		retval |= Service_Write_writeNode(channel->session->application, &request->nodesToWrite[i], &response->results[i]);
+	}
+
+	return retval;
+}

+ 16 - 0
src/ua_services_internal.h

@@ -0,0 +1,16 @@
+/**
+ * @brief This files contains helper functions for the UA-Services that are used
+ * internally as well (with a simplified API as no access rights are checked).
+ */
+
+#include "ua_namespace.h"
+#include "ua_types.h"
+#include "ua_types_generated.h"
+
+/* @brief Add a reference (and the inverse reference to the target node).
+ *
+ * @param The node to which the reference shall be added
+ * @param The reference itself
+ * @param The namespace where the target node is looked up for the reverse reference (this is omitted if targetns is UA_NULL)
+ */
+UA_Int32 AddReference(UA_Node *node, UA_ReferenceNode *reference, Namespace *targetns);

+ 17 - 8
src/ua_services_monitoreditems.c

@@ -1,24 +1,33 @@
 #include "ua_services.h"
+#include "ua_statuscodes.h"
 
-#if 0
+//#if 0
 /* Activate once the infrastructure for pushing events is in place. */
 
 UA_Int32 Service_CreateMonitoredItems(SL_Channel *channel, const UA_CreateMonitoredItemsRequest *request, UA_CreateMonitoredItemsResponse *response) {
 	if (request->itemsToCreateSize > 0) {
 		response->resultsSize = request->itemsToCreateSize;
-		UA_Array_new((void**)&(response->results),response->resultsSize,UA_MONITOREDITEMCREATERESULT);
+
+		UA_Array_new((void**)&(response->results),response->resultsSize,&UA_.types[UA_MONITOREDITEMCREATERESULT]);
 		for (int i=0;request->itemsToCreateSize > 0 && i < request->itemsToCreateSize;i++) {
-			UA_NodeId_printf("CreateMonitoredItems - itemToCreate=",&(request->itemsToCreate[i]->itemToMonitor.nodeId));
+			UA_NodeId_printf("CreateMonitoredItems - itemToCreate=",&(request->itemsToCreate[i].itemToMonitor.nodeId));
 			//FIXME: search the object in the namespace
-			if (request->itemsToCreate[i]->itemToMonitor.nodeId.identifier.numeric == 2253) { // server
-				response->results[i]->statusCode = UA_STATUSCODE_GOOD;
-				response->results[i]->monitoredItemId = 1024;
+
+			if (request->itemsToCreate[i].itemToMonitor.nodeId.identifier.numeric == 2253) { // server
+
+				response->results[i].statusCode = UA_STATUSCODE_GOOD;
+				response->results[i].monitoredItemId = 1;
+				response->results[i].revisedSamplingInterval = 4294967295;
+				response->results[i].revisedQueueSize = 0;
 			} else {
 				// response->results[i]->statusCode = UA_STATUSCODE_BAD_NODEIDUNKNOWN;
-				response->results[i]->statusCode = -1;
+
+				response->results[i].statusCode = -1;
 			}
 		}
 	}
+	//mock up
+	return UA_SUCCESS;
 }
 
-#endif
+//#endif

+ 51 - 0
src/ua_services_nodemanagement.c

@@ -1,6 +1,7 @@
 #include "ua_services.h"
 #include "ua_statuscodes.h"
 #include "ua_namespace.h"
+#include "ua_services_internal.h"
 
 #define CHECKED_ACTION(ACTION, CLEAN_UP, GOTO) do {	\
 	status |= ACTION; \
@@ -88,3 +89,53 @@ UA_Int32 Service_AddNodes(SL_Channel *channel, const UA_AddNodesRequest *request
 	return UA_SUCCESS;
 	
 }
+
+static UA_Int32 AddSingleReference(UA_Node *node, UA_ReferenceNode *reference) {
+	// TODO: Check if reference already exists
+
+	UA_Int32 count = node->referencesSize;
+	if(count < 0) count = 0;
+	UA_ReferenceNode *old_refs = node->references;
+	UA_ReferenceNode *new_refs;
+
+	UA_Int32 retval = UA_alloc((void **)&new_refs, sizeof(UA_ReferenceNode)*(count+1));
+	if(retval != UA_SUCCESS)
+		return UA_ERROR;
+	UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode)*count);
+	retval |= UA_ReferenceNode_copy(reference, &new_refs[count]);
+
+	if(retval != UA_SUCCESS) {
+		UA_free(new_refs);
+		return retval;
+	}
+	
+	node->references = new_refs;
+	node->referencesSize = count+1;
+	return retval;
+}
+
+UA_Int32 AddReference(UA_Node *node, UA_ReferenceNode *reference, Namespace *targetns) {
+	UA_Int32 retval = AddSingleReference(node, reference);
+	if(retval != UA_SUCCESS || targetns == UA_NULL)
+		return retval;
+
+	UA_Node *targetnode;
+	Namespace_Entry_Lock *lock;
+	// TODO: Nodes in the namespace are immutable (for lockless multithreading).
+	// Do a copy every time?
+	if(Namespace_get(targetns, &reference->targetId.nodeId, (const UA_Node**)&targetnode, &lock) != UA_SUCCESS)
+		return UA_ERROR;
+
+	UA_ReferenceNode inversereference;
+	inversereference.referenceTypeId = reference->referenceTypeId;
+	inversereference.isInverse = !reference->isInverse;
+	inversereference.targetId = (UA_ExpandedNodeId){node->nodeId, UA_STRING_NULL, 0};	
+	retval = AddSingleReference(targetnode, &inversereference);
+	Namespace_Entry_Lock_release(lock);
+
+	return retval;
+}
+
+UA_Int32 Service_AddReferences(SL_Channel *channel, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response) {
+	return UA_ERROR;
+}

+ 30 - 0
src/ua_services_subscription.c

@@ -0,0 +1,30 @@
+/*
+ * ua_services_subscription.c
+ *
+ *  Created on: 18.06.2014
+ *      Author: root
+ */
+#include "ua_services.h"
+#include "ua_statuscodes.h"
+
+UA_Int32 Service_CreateSubscription(SL_Channel *channel, const UA_CreateSubscriptionRequest *request,
+                                   UA_CreateSubscriptionResponse *response)
+{
+
+	response->subscriptionId = 42;
+	response->revisedPublishingInterval = 100000;
+	response->revisedLifetimeCount = 120000;
+	response->revisedMaxKeepAliveCount = 50;
+	return UA_SUCCESS;
+}
+
+UA_Int32 Service_Publish(SL_Channel *channel, const UA_PublishRequest *request,
+                                   UA_PublishResponse *response)
+{
+
+	response->subscriptionId = 42;
+	response->notificationMessage.sequenceNumber = 1;
+	response->notificationMessage.publishTime = UA_DateTime_now();
+	return UA_SUCCESS;
+}
+

+ 215 - 11
src/ua_services_view.c

@@ -1,30 +1,234 @@
 #include "ua_services.h"
 #include "ua_statuscodes.h"
 
+UA_Int32 Service_Browse_getReferenceDescription(Namespace *ns, UA_ReferenceNode* reference, UA_UInt32 nodeClassMask,
+												UA_UInt32 resultMask, UA_ReferenceDescription* referenceDescription) {
+	const UA_Node* foundNode;
+	Namespace_Entry_Lock *lock;
+
+	if(Namespace_get(ns,&reference->targetId.nodeId,&foundNode, &lock) != UA_SUCCESS)
+		return UA_ERROR;
+
+	UA_NodeId_copy(&foundNode->nodeId, &referenceDescription->nodeId.nodeId);
+	//TODO ExpandedNodeId is a mockup
+	referenceDescription->nodeId.serverIndex = 0;
+	referenceDescription->nodeId.namespaceUri.length = -1;
+
+	UA_UInt32 mask = 0;
+	for (mask = 0x01; mask <= 0x40; mask *= 2) {
+		switch (mask & (resultMask)) {
+		case UA_BROWSERESULTMASK_REFERENCETYPEID:
+			UA_NodeId_copy(&reference->referenceTypeId, &referenceDescription->referenceTypeId);
+			break;
+		case UA_BROWSERESULTMASK_ISFORWARD:
+			referenceDescription->isForward = !reference->isInverse;
+			break;
+		case UA_BROWSERESULTMASK_NODECLASS:
+			UA_NodeClass_copy(&foundNode->nodeClass, &referenceDescription->nodeClass);
+			break;
+		case UA_BROWSERESULTMASK_BROWSENAME:
+			UA_QualifiedName_copy(&foundNode->browseName, &referenceDescription->browseName);
+			break;
+		case UA_BROWSERESULTMASK_DISPLAYNAME:
+			UA_LocalizedText_copy(&foundNode->displayName, &referenceDescription->displayName);
+			break;
+		case UA_BROWSERESULTMASK_TYPEDEFINITION:
+			if (foundNode->nodeClass != UA_NODECLASS_OBJECT &&
+				foundNode->nodeClass != UA_NODECLASS_VARIABLE)
+				break;
+
+			for(UA_Int32 i = 0;i<foundNode->referencesSize;i++) {
+				UA_ReferenceNode *ref = &foundNode->references[i];
+				if(ref->referenceTypeId.identifier.numeric == 40 /* hastypedefinition */) {
+					UA_ExpandedNodeId_copy(&ref->targetId, &referenceDescription->typeDefinition);
+					break;
+				}
+			}
+			break;
+		}
+	}
+	
+	Namespace_Entry_Lock_release(lock);
+	return UA_SUCCESS;
+}
+
+/* singly-linked list */
+struct SubRefTypeId {
+	UA_NodeId id;
+	UA_SLIST_ENTRY(SubRefTypeId) next;
+};
+UA_SLIST_HEAD(SubRefTypeIdList, SubRefTypeId);
+
+UA_UInt32 walkReferenceTree(Namespace *ns, UA_ReferenceTypeNode *current, struct SubRefTypeIdList *list) {
+	// insert the current referencetype
+	struct SubRefTypeId *element;
+	UA_alloc((void**)&element, sizeof(struct SubRefTypeId));
+	element->id = current->nodeId;
+	UA_SLIST_INSERT_HEAD(list, element, next);
+
+	UA_UInt32 count = 1; // the current element
+
+	// walk the tree
+	for(UA_Int32 i = 0;i < current->referencesSize;i++) {
+		if(current->references[i].referenceTypeId.identifier.numeric == 45 /* HasSubtype */ &&
+		   current->references[i].isInverse == UA_FALSE) {
+			const UA_Node *node;
+			Namespace_Entry_Lock *lock;
+			if(Namespace_get(ns, &current->references[i].targetId.nodeId, &node, &lock) == UA_SUCCESS &&
+			   node->nodeClass == UA_NODECLASS_REFERENCETYPE) {
+				count += walkReferenceTree(ns,(UA_ReferenceTypeNode*)node, list);
+				Namespace_Entry_Lock_release(lock);
+			}
+		}
+	}
+	return count;
+}
+
+/* We do not search across namespaces so far. The id of the father-referencetype is returned in the array also. */
+static UA_Int32 findSubReferenceTypes(Namespace *ns, UA_NodeId *rootReferenceType, UA_NodeId **ids, UA_UInt32 *idcount) {
+	struct SubRefTypeIdList list;
+	UA_SLIST_INIT(&list);
+
+	// walk the tree
+	UA_ReferenceTypeNode *root;
+	Namespace_Entry_Lock *lock;
+	if(Namespace_get(ns, rootReferenceType, (const UA_Node**)&root, &lock) != UA_SUCCESS ||
+	   root->nodeClass != UA_NODECLASS_REFERENCETYPE)
+		return UA_ERROR;
+	UA_UInt32 count = walkReferenceTree(ns, root, &list);
+	Namespace_Entry_Lock_release(lock);
+
+	// copy results into an array
+	UA_alloc((void**) ids, sizeof(UA_NodeId)*count);
+	for(UA_UInt32 i = 0; i < count;i++) {
+		struct SubRefTypeId *element = UA_SLIST_FIRST(&list);
+		UA_NodeId_copy(&element->id, &(*ids)[i]);
+		UA_SLIST_REMOVE_HEAD(&list, next);
+		UA_free(element);
+	}
+	*idcount = count;
+
+	return UA_SUCCESS;
+}
+
+/* is this a relevant reference? */
+static inline UA_Boolean Service_Browse_returnReference(UA_BrowseDescription *browseDescription, UA_ReferenceNode* reference,
+														UA_NodeId *relevantRefTypes, UA_UInt32 relevantRefTypesCount) {
+	if (reference->isInverse == UA_TRUE && browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
+		return UA_FALSE;
+	else if (reference->isInverse == UA_FALSE && browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
+		return UA_FALSE;
+	for(UA_UInt32 i = 0; i<relevantRefTypesCount;i++) {
+		if(UA_NodeId_equal(&browseDescription->referenceTypeId, &relevantRefTypes[i]) == UA_EQUAL)
+			return UA_TRUE;
+	}
+	return UA_FALSE;
+}
+
+/* Return results to a single browsedescription. */
+static void Service_Browse_getBrowseResult(Namespace *ns, UA_BrowseDescription *browseDescription,
+										   UA_UInt32 maxReferences, UA_BrowseResult *browseResult) {
+	const UA_Node* node;
+	Namespace_Entry_Lock *lock;
+
+	if(Namespace_get(ns, &browseDescription->nodeId, &node, &lock) != UA_SUCCESS) {
+		browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+		return;
+	}
+
+	// 0 => unlimited references
+	if(maxReferences == 0)
+		maxReferences = node->referencesSize;
+
+	// discover the relevant subtypes
+	UA_NodeId *relevantReferenceTypes;
+	UA_UInt32 relevantReferenceTypesCount;
+	if(!browseDescription->includeSubtypes ||
+	   findSubReferenceTypes(ns, &browseDescription->referenceTypeId, &relevantReferenceTypes, &relevantReferenceTypesCount) != UA_SUCCESS) {
+		UA_alloc((void**)&relevantReferenceTypes, sizeof(UA_NodeId));
+		UA_NodeId_copy(&browseDescription->referenceTypeId, relevantReferenceTypes);
+		relevantReferenceTypesCount = 1;
+	}
+
+	/* We do not use a linked list but traverse the nodes references list twice
+	 * (once for counting, once for generating the referencedescriptions). That
+	 * is much faster than using a linked list, since the references are
+	 * allocated in a continuous blob and RAM access is predictible/does not
+	 * miss cache lines so often. TODO: measure with some huge objects! */
+	UA_UInt32 refs = 0;
+	for(UA_Int32 i=0;i < node->referencesSize && refs <= maxReferences;i++) {
+		if(Service_Browse_returnReference(browseDescription, &node->references[i], relevantReferenceTypes, relevantReferenceTypesCount))
+			refs++;
+	}
+
+	// can we return all relevant references at once?
+	UA_Boolean finished = UA_TRUE;
+	if(refs > maxReferences) {
+		refs--;
+		finished = UA_FALSE;
+	}
+	
+	browseResult->referencesSize = refs;
+	UA_Array_new((void**) &browseResult->references, refs, &UA_.types[UA_REFERENCEDESCRIPTION]);
+	
+	for(UA_UInt32 i = 0, j=0; j < refs; i++) {
+		if(!Service_Browse_returnReference(browseDescription, &node->references[i], relevantReferenceTypes, relevantReferenceTypesCount))
+			continue;
+		
+		if(Service_Browse_getReferenceDescription(ns, &node->references[i], browseDescription->nodeClassMask,
+												  browseDescription->resultMask, &browseResult->references[j]) != UA_SUCCESS)
+			browseResult->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
+		j++;
+	}
+
+	if(!finished) {
+		// Todo. Set the Statuscode and the continuation point.
+	}
+	
+	Namespace_Entry_Lock_release(lock);
+	UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesCount, &UA_.types[UA_NODEID]);
+}
+
 UA_Int32 Service_Browse(SL_Channel *channel, const UA_BrowseRequest *request, UA_BrowseResponse *response) {
 	UA_Int32 retval = UA_SUCCESS;
 	DBG_VERBOSE(UA_NodeId_printf("BrowseService - view=", &request->view.viewId));
-	UA_Int32 i = 0;
-	for (i=0;request->nodesToBrowseSize > 0 && i<request->nodesToBrowseSize;i++) {
-		UA_NodeId_printf("BrowseService - nodesToBrowse=", &request->nodesToBrowse[i].nodeId);
+
+	//TODO request->view not used atm
+	UA_Array_new((void**) &(response->results), request->nodesToBrowseSize, &UA_.types[UA_BROWSERESULT]);
+	response->resultsSize = request->nodesToBrowseSize;
+
+	for(UA_Int32 i=0; i < request->nodesToBrowseSize; i++) {
+		Namespace *ns = UA_indexedList_findValue(channel->session->application->namespaces,
+												 request->nodesToBrowse[i].nodeId.namespace);
+		if(ns == UA_NULL) {
+			response->results[i].statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+			continue;
+		}
+		
+		// Service_Browse_getBrowseResult has no return value. All errors are resolved internally.
+		Service_Browse_getBrowseResult(ns, &request->nodesToBrowse[i],
+									   request->requestedMaxReferencesPerNode, &response->results[i]);
 	}
+
+	//TODO fill Diagnostic info array
+	response->diagnosticInfosSize = 0;
+	response->diagnosticInfos = UA_NULL;
 	return retval;
 }
 
-UA_Int32 Service_TranslateBrowsePathsToNodeIds(SL_Channel *channel, const UA_TranslateBrowsePathsToNodeIdsRequest *request, UA_TranslateBrowsePathsToNodeIdsResponse *response)
-{
+UA_Int32 Service_TranslateBrowsePathsToNodeIds(SL_Channel *channel, const UA_TranslateBrowsePathsToNodeIdsRequest *request,
+											   UA_TranslateBrowsePathsToNodeIdsResponse *response) {
 	UA_Int32 retval = UA_SUCCESS;
-
 	DBG_VERBOSE(printf("TranslateBrowsePathsToNodeIdsService - %i path(s)", request->browsePathsSize));
-	//Allocate space for a correct answer
-	UA_Array_new((void**)&response->results, request->browsePathsSize, &UA_.types[UA_BROWSEPATHRESULT]);
 
+	// Allocate space for a correct answer
 	response->resultsSize = request->browsePathsSize;
+	// _init of the elements is done in Array_new
+	UA_Array_new((void**) &response->results, request->browsePathsSize, &UA_.types[UA_BROWSEPATHRESULT]);
 
-	for(UA_Int32 i = 0;i<request->browsePathsSize;i++){
-		UA_BrowsePathResult_init(&response->results[i]);
+	for (UA_Int32 i = 0; i < request->browsePathsSize; i++) {
 		//FIXME: implement
-		response->results[i].statusCode = UA_STATUSCODE_BADQUERYTOOCOMPLEX;
+		response->results[i].statusCode = UA_STATUSCODE_BADNOMATCH;
 	}
 
 	return retval;

+ 21 - 19
src/ua_transport_binary.c

@@ -94,26 +94,28 @@ UA_Int32 TL_Process(TL_Connection* connection, const UA_ByteString* msg) {
 	UA_OPCUATcpMessageHeader tcpMessageHeader;
 
 	DBG_VERBOSE(printf("TL_Process - entered \n"));
-	if ((retval = UA_OPCUATcpMessageHeader_decodeBinary(msg,&pos,&tcpMessageHeader)) == UA_SUCCESS) {
-		printf("TL_Process - messageType=%.*s\n",3,msg->data);
-		switch(tcpMessageHeader.messageType) {
-		case UA_MESSAGETYPE_HEL:
-			retval = TL_handleHello(connection, msg, &pos);
-			break;
-		case UA_MESSAGETYPE_OPN:
-			retval = TL_handleOpen(connection, msg, &pos);
-			break;
-		case UA_MESSAGETYPE_MSG:
-			retval = TL_handleMsg(connection, msg, &pos);
-			break;
-		case UA_MESSAGETYPE_CLO:
-			retval = TL_handleClo(connection, msg, &pos);
-			break;
-		default: // dispatch processing to secureLayer
-			retval = UA_ERR_INVALID_VALUE;
-			break;
+	do{
+		if ((retval = UA_OPCUATcpMessageHeader_decodeBinary(msg,&pos,&tcpMessageHeader)) == UA_SUCCESS) {
+			printf("TL_Process - messageType=%.*s\n",3,msg->data);
+			switch(tcpMessageHeader.messageType) {
+			case UA_MESSAGETYPE_HEL:
+				retval = TL_handleHello(connection, msg, &pos);
+				break;
+			case UA_MESSAGETYPE_OPN:
+				retval = TL_handleOpen(connection, msg, &pos);
+				break;
+			case UA_MESSAGETYPE_MSG:
+				retval = TL_handleMsg(connection, msg, &pos);
+				break;
+			case UA_MESSAGETYPE_CLO:
+				retval = TL_handleClo(connection, msg, &pos);
+				break;
+			default: // dispatch processing to secureLayer
+				retval = UA_ERR_INVALID_VALUE;
+				break;
+			}
 		}
-	}
+	}while(msg->length > (UA_Int32)pos);
 	/* if (retval != UA_SUCCESS) { */
 	/* 	// FIXME: compose real error message */
 	/* 	UA_ByteString errorMsg; */

+ 19 - 3
src/ua_transport_binary_secure.c

@@ -101,9 +101,9 @@ static void init_response_header(UA_RequestHeader const *p, UA_ResponseHeader *r
     DBG_VERBOSE(printf("Invoke Service: %s\n", # TYPE));                             \
     Service_##TYPE(channel, &p, &r);                                                 \
     DBG_VERBOSE(printf("Finished Service: %s\n", # TYPE));                           \
-    *offset = 0;                                                                     \
+    UA_UInt32 sending_offset = 0;													 \
     UA_ByteString_newMembers(&response_msg, UA_##TYPE##Response_calcSizeBinary(&r)); \
-    UA_##TYPE##Response_encodeBinary(&r, &response_msg, offset);                     \
+    UA_##TYPE##Response_encodeBinary(&r, &response_msg, &sending_offset);            \
     UA_##TYPE##Request_deleteMembers(&p);                                            \
     UA_##TYPE##Response_deleteMembers(&r);                                           \
 
@@ -140,10 +140,26 @@ UA_Int32 SL_handleRequest(SL_Channel *channel, const UA_ByteString *msg, UA_UInt
 	}else if(serviceid == UA_READREQUEST_NS0) {
 		INVOKE_SERVICE(Read);
 		responsetype = UA_READRESPONSE_NS0;
+	}else if(serviceid == UA_WRITEREQUEST_NS0)
+	{
+		INVOKE_SERVICE(Write);
+		responsetype = UA_WRITERESPONSE_NS0;
 	}else if(serviceid == UA_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_NS0) {
 		INVOKE_SERVICE(TranslateBrowsePathsToNodeIds);
 		responsetype = UA_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE_NS0;
-	}else {
+	}else if(serviceid == UA_BROWSEREQUEST_NS0) {
+		INVOKE_SERVICE(Browse);
+		responsetype = UA_BROWSERESPONSE_NS0;
+	}else if(serviceid == UA_CREATESUBSCRIPTIONREQUEST_NS0) {
+		INVOKE_SERVICE(CreateSubscription);
+		responsetype = UA_CREATESUBSCRIPTIONRESPONSE_NS0;
+	}else if(serviceid == UA_CREATEMONITOREDITEMSREQUEST_NS0) {
+		INVOKE_SERVICE(CreateMonitoredItems);
+		responsetype = UA_CREATEMONITOREDITEMSRESPONSE_NS0;
+	}else if(serviceid == UA_PUBLISHREQUEST_NS0) {
+		INVOKE_SERVICE(Publish);
+		responsetype = UA_PUBLISHRESPONSE_NS0;
+	}else{
 		printf("SL_processMessage - unknown request, namespace=%d, request=%d\n",
 		       serviceRequestType.namespace,
 		       serviceRequestType.identifier.numeric);

+ 1 - 1
src/ua_types.c

@@ -511,7 +511,7 @@ void UA_NodeId_printf(char *label, const UA_NodeId *node) {
 }
 
 UA_Int32 UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2) {
-	if(n1 == UA_NULL || n2 == UA_NULL || n1->encodingByte != n2->encodingByte || n1->namespace != n2->namespace)
+	if(n1 == UA_NULL || n2 == UA_NULL || n1->namespace != n2->namespace)
 		return UA_NOT_EQUAL;
 
 	switch(n1->encodingByte & UA_NODEIDTYPE_MASK) {

+ 12 - 2
src/ua_types.h

@@ -119,6 +119,9 @@ typedef struct UA_NodeId {
 	} identifier;
 } UA_NodeId;
 
+#define NS0NODEID(NUMERIC_ID) \
+	(UA_NodeId){.encodingByte = 0 /*UA_NODEIDTYPE_TWOBYTE*/, .namespace = 0, .identifier.numeric = NUMERIC_ID}
+
 #define UA_NODEIDTYPE_NAMESPACE_URI_FLAG 0x80
 #define UA_NODEIDTYPE_SERVERINDEX_FLAG 0x40
 #define UA_NODEIDTYPE_MASK (~(UA_NODEIDTYPE_NAMESPACE_URI_FLAG | UA_NODEIDTYPE_SERVERINDEX_FLAG))
@@ -130,6 +133,9 @@ typedef struct UA_ExpandedNodeId {
 	UA_UInt32 serverIndex;
 } UA_ExpandedNodeId;
 
+#define NS0EXPANDEDNODEID(NUMERIC_ID) \
+	(UA_ExpandedNodeId){.nodeId = NS0NODEID(NUMERIC_ID), .namespaceUri = {-1, UA_NULL}, .serverIndex = 0}
+
 /** @brief A numeric identifier for a error or condition that is associated with a value or an operation. */
 typedef UA_UInt32 UA_StatusCode; // StatusCodes aren't an enum(=int) since 32 unsigned bits are needed. See also ua_statuscodes.h */
 
@@ -272,6 +278,9 @@ UA_TYPE_PROTOTYPES(UA_DiagnosticInfo)
 UA_TYPE_PROTOTYPES(UA_InvalidType)
 
 /* String */
+#define UA_STRING_NULL (UA_String){-1, UA_NULL}
+#define UA_STRING_STATIC(STRING) (UA_String){sizeof(STRING)-1, (UA_Byte*)STRING}
+
 UA_Int32 UA_String_copycstring(char const *src, UA_String *dst);
 UA_Int32 UA_String_copyprintf(char const *fmt, UA_String *dst, ...);
 UA_Int32 UA_String_equal(const UA_String *string1, const UA_String *string2);
@@ -309,17 +318,18 @@ void UA_ByteString_printx_hex(char *label, const UA_ByteString *string);
 UA_Int32 UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
 void UA_NodeId_printf(char *label, const UA_NodeId *node);
 UA_Boolean UA_NodeId_isNull(const UA_NodeId *p);
-UA_Int16 UA_NodeId_getNamespace(UA_NodeId const *id);
-UA_Int16 UA_NodeId_getIdentifier(UA_NodeId const *id);
 UA_Boolean UA_NodeId_isBasicType(UA_NodeId const *id);
 
 /* ExpandedNodeId */
 UA_Boolean UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
 
 /* QualifiedName */
+#define UA_QUALIFIEDNAME_STATIC(STRING) \
+	(UA_QualifiedName){0, {sizeof(STRING)-1, (UA_Byte*)STRING}}
 void UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn);
 
 /* LocalizedText */
+#define UA_LOCALIZEDTEXT_STATIC(STRING) (UA_LocalizedText){{2, (UA_Byte*)"en"}, UA_STRING_STATIC(STRING)}
 UA_Int32 UA_LocalizedText_copycstring(char const *src, UA_LocalizedText *dst);
 
 /* Variant */

+ 117 - 24
src/ua_types_encoding_binary.c

@@ -1,29 +1,60 @@
 #include "ua_types_encoding_binary.h"
 #include "ua_namespace_0.h"
 
+static inline UA_Boolean is_builtin(UA_NodeId *typeid) {
+	return (typeid->namespace == 0 && 1 <= typeid->identifier.numeric && typeid->identifier.numeric <= 25);
+}
+
 /*********/
 /* Array */
 /*********/
 
 UA_Int32 UA_Array_calcSizeBinary(UA_Int32 nElements, UA_VTable_Entry *vt, const void *data) {
-	if(vt == UA_NULL || data == UA_NULL)
+	if(vt == UA_NULL){
 		return 0; // do not return error as the result will be used to allocate memory
+	}
+	if(data == UA_NULL){ //NULL Arrays are encoded as length = -1
+		return sizeof(UA_Int32);
+	}
+	UA_Int32  length     = sizeof(UA_Int32);
+	UA_UInt32 memSize    = vt->memSize;
+	const UA_Byte *cdata = (const UA_Byte *)data;
+	for(UA_Int32 i = 0;i < nElements;i++) {
+		length += vt->encodings[UA_ENCODING_BINARY].calcSize(cdata);
+		cdata  += memSize;
+	}
+	return length;
+}
 
+static UA_Int32 UA_Array_calcSizeBinary_asExtensionObject(UA_Int32 nElements, UA_VTable_Entry *vt,
+														   const void *data) {
+	if(vt == UA_NULL){
+		return 0; // do not return error as the result will be used to allocate memory
+	}
+	if(data == UA_NULL){ //NULL Arrays are encoded as length = -1
+		return sizeof(UA_Int32);
+	}
 	UA_Int32  length     = sizeof(UA_Int32);
 	UA_UInt32 memSize    = vt->memSize;
+	UA_Boolean isBuiltin = is_builtin(&vt->typeId);
 	const UA_Byte *cdata = (const UA_Byte *)data;
 	for(UA_Int32 i = 0;i < nElements;i++) {
 		length += vt->encodings[UA_ENCODING_BINARY].calcSize(cdata);
 		cdata  += memSize;
 	}
+	if(isBuiltin)
+		length += 9*nElements; // extensionobject header for each element
 	return length;
 }
 
 UA_Int32 UA_Array_encodeBinary(const void *src, UA_Int32 noElements, UA_VTable_Entry *vt, UA_ByteString *dst,
                                UA_UInt32 *offset) {
-	if(vt == UA_NULL || src == UA_NULL || dst == UA_NULL || offset == UA_NULL)
+	if(vt == UA_NULL || dst == UA_NULL || offset == UA_NULL || ((src == UA_NULL) && (noElements > 0)))
 		return UA_ERROR;
 
+	//Null Arrays are encoded with length = -1 // part 6 - §5.24
+	if(noElements < -1) noElements = -1;
+
 	UA_Int32 retval     = UA_SUCCESS;
 	retval = UA_Int32_encodeBinary(&noElements, dst, offset);
 	const UA_Byte *csrc = (const UA_Byte *)src;
@@ -35,6 +66,34 @@ UA_Int32 UA_Array_encodeBinary(const void *src, UA_Int32 noElements, UA_VTable_E
 	return retval;
 }
 
+static UA_Int32 UA_Array_encodeBinary_asExtensionObject(const void *src, UA_Int32 noElements, UA_VTable_Entry *vt,
+														UA_ByteString *dst, UA_UInt32 *offset) {
+	if(vt == UA_NULL || dst == UA_NULL || offset == UA_NULL || (src == UA_NULL && noElements > 0))
+		return UA_ERROR;
+
+	//Null Arrays are encoded with length = -1 // part 6 - §5.24
+	if(noElements < -1) noElements = -1;
+
+	UA_Int32 retval     = UA_SUCCESS;
+	retval = UA_Int32_encodeBinary(&noElements, dst, offset);
+	const UA_Byte *csrc = (const UA_Byte *)src;
+	UA_UInt32 memSize   = vt->memSize;
+	UA_Boolean isBuiltin = is_builtin(&vt->typeId);
+	for(UA_Int32 i = 0;i < noElements && retval == UA_SUCCESS;i++) {
+		if(!isBuiltin) {
+			// print the extensionobject header
+			UA_NodeId_encodeBinary(&vt->typeId, dst, offset);
+			UA_Byte eoEncoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
+			UA_Byte_encodeBinary(&eoEncoding, dst, offset);
+			UA_Int32 eoEncodingLength = vt->encodings[UA_ENCODING_BINARY].calcSize(csrc);
+			UA_Int32_encodeBinary(&eoEncodingLength, dst, offset);
+		}
+		retval |= vt->encodings[UA_ENCODING_BINARY].encode(csrc, dst, offset);
+		csrc   += memSize;
+	}
+	return retval;
+}
+
 UA_Int32 UA_Array_decodeBinary(const UA_ByteString *src, UA_UInt32 *offset, UA_Int32 noElements, UA_VTable_Entry *vt,
                                void **dst) {
 	if(vt == UA_NULL || src == UA_NULL || dst == UA_NULL || offset == UA_NULL)
@@ -663,28 +722,42 @@ UA_Int32 UA_DataValue_calcSizeBinary(UA_DataValue const *p) {
 }
 
 /* Variant */
-UA_Int32 UA_Variant_calcSizeBinary(UA_Variant const *p) {
-	if(p == UA_NULL)
-		return sizeof(UA_Variant);
+/* We can store all data types in a variant internally. But for communication we
+ * encode them in an ExtensionObject if they are not one of the built in types.
+ * Officially, only builtin types are contained in a variant.
+ *
+ * Every ExtensionObject incurrs an overhead of 4 byte (nodeid) + 1 byte (encoding) */
+UA_Int32 UA_Variant_calcSizeBinary(UA_Variant const *p) { if(p == UA_NULL)
+   return sizeof(UA_Variant);
 
 	if(p->vt == UA_NULL)
 		return UA_ERR_INCONSISTENT;
+	UA_Int32 arrayLength = p->arrayLength;
+	if(p->data == UA_NULL)
+		arrayLength = -1;
 
-	UA_Boolean isArray = p->arrayLength != 1;       // a single element is not an array
+	UA_Boolean isArray = arrayLength != 1;       // a single element is not an array
 	UA_Boolean hasDimensions = isArray && p->arrayDimensions != UA_NULL;
+	UA_Boolean isBuiltin = is_builtin(&p->vt->typeId);
 
 	UA_Int32   length        = sizeof(UA_Byte); //p->encodingMask
 	if(isArray) {
 		// array length + the array itself
-		length += UA_Array_calcSizeBinary(p->arrayLength, p->vt, p->data);
+		length += UA_Array_calcSizeBinary(arrayLength, p->vt, p->data);
 	} else {
-		// if p->data is null, encoding will return an error anyway.
-		if(p->data != UA_NULL)
-			length += p->vt->encodings[UA_ENCODING_BINARY].calcSize(p->data);
+		length += p->vt->encodings[UA_ENCODING_BINARY].calcSize(p->data);
+		if(!isBuiltin)
+			length += 9; // 4 byte nodeid + 1 byte encoding + 4 byte bytestring length
 	}
 
-	if(hasDimensions)
-		length += UA_Array_calcSizeBinary(p->arrayDimensionsLength, &UA_.types[UA_INT32], p->arrayDimensions);
+	if(hasDimensions) {
+		if(isBuiltin)
+			length += UA_Array_calcSizeBinary(p->arrayDimensionsLength, &UA_.types[UA_INT32], p->arrayDimensions);
+		else
+			length += UA_Array_calcSizeBinary_asExtensionObject(p->arrayDimensionsLength, &UA_.types[UA_INT32],
+																p->arrayDimensions);
+	}
+			
 
 	return length;
 }
@@ -695,29 +768,49 @@ UA_TYPE_ENCODEBINARY(UA_Variant,
 
                      UA_Boolean isArray       = src->arrayLength != 1;  // a single element is not an array
                      UA_Boolean hasDimensions = isArray && src->arrayDimensions != UA_NULL;
+					 UA_Boolean isBuiltin = is_builtin(&src->vt->typeId);
 
                      UA_Byte encodingByte = 0;
                      if(isArray) {
-                         encodingByte |= (0x01 << 7);
+                         encodingByte |= UA_VARIANT_ENCODINGMASKTYPE_ARRAY;
                          if(hasDimensions)
-							 encodingByte |= (0x01 << 6);
+							 encodingByte |= UA_VARIANT_ENCODINGMASKTYPE_DIMENSIONS;
 					 }
-                     encodingByte |= 0x3F & (UA_Byte)src->vt->typeId.identifier.numeric;;
+
+					 if(isBuiltin) {
+						 encodingByte |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (UA_Byte)src->vt->typeId.identifier.numeric;
+					 } else
+						 encodingByte |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (UA_Byte)22; // ExtensionObject
+					 
                      retval |= UA_Byte_encodeBinary(&encodingByte, dst, offset);
 
                      if(isArray)
 						 retval |= UA_Array_encodeBinary(src->data, src->arrayLength, src->vt, dst, offset);
-                     else {
-                         if(src->data == UA_NULL)
+                     else if(src->data == UA_NULL)
 							 retval = UA_ERROR;       // an array can be empty. a single element must be present.
-                         retval |= src->vt->encodings[UA_ENCODING_BINARY].encode(src->data, dst, offset);
+					 else {
+						 if(!isBuiltin) {
+							 // print the extensionobject header
+							 UA_NodeId_encodeBinary(&src->vt->typeId, dst, offset);
+							 UA_Byte eoEncoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
+							 UA_Byte_encodeBinary(&eoEncoding, dst, offset);
+							 UA_Int32 eoEncodingLength = src->vt->encodings[UA_ENCODING_BINARY].calcSize(src->data);
+							 UA_Int32_encodeBinary(&eoEncodingLength, dst, offset);
+						 }
+						 retval |= src->vt->encodings[UA_ENCODING_BINARY].encode(src->data, dst, offset);
 					 }
 
-                     if(hasDimensions)
-						 retval |= UA_Array_encodeBinary(src->arrayDimensions, src->arrayDimensionsLength,
-						                                 &UA_.types[UA_INT32], dst, offset);
-                     )
-
+                     if(hasDimensions) {
+						 if(isBuiltin)
+							 retval |= UA_Array_encodeBinary(src->arrayDimensions, src->arrayDimensionsLength,
+															 &UA_.types[UA_INT32], dst, offset);
+						 else
+							 retval |= UA_Array_encodeBinary_asExtensionObject(src->arrayDimensions,
+																			   src->arrayDimensionsLength,
+																			   &UA_.types[UA_INT32], dst, offset);
+					 })
+
+/* For decoding, we read extensionobects as is. */
 UA_Int32 UA_Variant_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset, UA_Variant * dst) {
 	UA_Int32 retval = UA_SUCCESS;
 	UA_Variant_init(dst);
@@ -726,7 +819,7 @@ UA_Int32 UA_Variant_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset, UA
 	CHECKED_DECODE(UA_Byte_decodeBinary(src, offset, &encodingByte),; );
 
 	UA_Boolean isArray = encodingByte & (0x01 << 7);                             // Bit 7
-	UA_Boolean hasDimensions = isArray && encodingByte & (0x01 << 6);            // Bit 6
+	UA_Boolean hasDimensions = isArray && (encodingByte & (0x01 << 6));            // Bit 6
 
 	UA_NodeId typeid = { .encodingByte = (UA_Byte)UA_NODEIDTYPE_FOURBYTE, .namespace= 0,
 						 .identifier.numeric = encodingByte & 0x3F };

+ 2 - 0
src/ua_types_encoding_binary.h

@@ -90,6 +90,8 @@ UA_TYPE_BINARY_ENCODING(UA_ExtensionObject)
 UA_TYPE_BINARY_ENCODING(UA_DataValue)
 UA_TYPE_BINARY_ENCODING(UA_Variant)
 UA_TYPE_BINARY_ENCODING(UA_DiagnosticInfo)
+
+/* Not built-in types */
 UA_TYPE_BINARY_ENCODING(UA_InvalidType)
 
 /*********/

+ 2 - 0
src/ua_types_encoding_xml.h

@@ -70,6 +70,8 @@ UA_TYPE_XML_ENCODING(UA_ExtensionObject)
 UA_TYPE_XML_ENCODING(UA_DataValue)
 UA_TYPE_XML_ENCODING(UA_Variant)
 UA_TYPE_XML_ENCODING(UA_DiagnosticInfo)
+
+/* Not built-in types */
 UA_TYPE_XML_ENCODING(UA_InvalidType)
 
 #endif /* UA_TYPES_ENCODING_XML_H_ */

+ 3 - 3
tests/check_base64.c

@@ -5,7 +5,7 @@
 START_TEST(base64_test_2padding)
 {
 	//this is base64'd ASCII string "open62541!"
-	UA_String encodedString = {16, "b3BlbjYyNTQxIQ=="};
+	UA_String encodedString = UA_STRING_STATIC("b3BlbjYyNTQxIQ==");
 
 	//assure that we allocate exactly 10 bytes
 	ck_assert_int_eq(UA_base64_getDecodedSize(&encodedString), 10);
@@ -34,7 +34,7 @@ START_TEST(base64_test_1padding)
 {
 
 	//this is base64'd ASCII string "open62541!!"
-	UA_String encodedString = {16, "b3BlbjYyNTQxISE="};
+	UA_String encodedString = UA_STRING_STATIC("b3BlbjYyNTQxISE=");
 
 	//assure that we allocate exactly 11 bytes
 	ck_assert_int_eq(UA_base64_getDecodedSize(&encodedString), 11);
@@ -64,7 +64,7 @@ START_TEST(base64_test_0padding)
 {
 
 	//this is base64'd ASCII string "open62541"
-	UA_String encodedString = {12, "b3BlbjYyNTQx"};
+	UA_String encodedString = UA_STRING_STATIC("b3BlbjYyNTQx");
 
 	//assure that we allocate exactly 9 bytes
 	ck_assert_int_eq(UA_base64_getDecodedSize(&encodedString), 9);

+ 1 - 1
tests/check_builtin.c

@@ -1566,7 +1566,7 @@ START_TEST(UA_Variant_copyShallWorkOnSingleValueExample) {
 
 	//then
 	UA_String copiedString = *(UA_String*)(copiedValue.data);
-	for(UA_Int32 i = 0;i < 3;i++)
+	for(UA_Int32 i = 0;i < 5;i++)
 		ck_assert_int_eq(copiedString.data[i], testString.data[i]);
 	ck_assert_int_eq(copiedString.length, testString.length);
 

+ 1 - 1
tests/check_services_view.c

@@ -30,7 +30,7 @@ START_TEST(Service_TranslateBrowsePathsToNodeIds_SmokeTest)
 	Service_TranslateBrowsePathsToNodeIds(UA_NULL,&request,&response);
 
 	ck_assert_int_eq(response.resultsSize,request.browsePathsSize);
-	ck_assert_int_eq(response.results[0].statusCode,UA_STATUSCODE_BADQUERYTOOCOMPLEX);
+	ck_assert_int_eq(response.results[0].statusCode,UA_STATUSCODE_BADNOMATCH);
 
 	//finally
 	UA_TranslateBrowsePathsToNodeIdsRequest_deleteMembers(&request);

+ 9 - 9
tools/generate_builtin.py

@@ -16,8 +16,8 @@ ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
 tree = etree.parse(sys.argv[1])
 types = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
 
-fh = open(sys.argv[2] + ".h",'w');
-fc = open(sys.argv[2] + ".c",'w');
+fh = open(sys.argv[2] + ".h",'w')
+fc = open(sys.argv[2] + ".c",'w')
 
 # dirty hack. we go up the call frames to access local variables of the calling
 # function. this allows to shorten code and get %()s replaces with less clutter.
@@ -29,10 +29,10 @@ def printc(string):
 
 # types that are coded manually 
 existing_types = set(["Boolean", "SByte", "Byte", "Int16", "UInt16", "Int32", "UInt32",
-                     "Int64", "UInt64", "Float", "Double", "String", "DateTime", "Guid",
-                     "ByteString", "XmlElement", "NodeId", "ExpandedNodeId", "StatusCode", 
-                     "QualifiedName", "LocalizedText", "ExtensionObject", "DataValue",
-                     "Variant", "DiagnosticInfo"])
+                      "Int64", "UInt64", "Float", "Double", "String", "DateTime", "Guid",
+                      "ByteString", "XmlElement", "NodeId", "ExpandedNodeId", "StatusCode", 
+                      "QualifiedName", "LocalizedText", "ExtensionObject", "DataValue",
+                      "Variant", "DiagnosticInfo"])
 
 fixed_size = set(["UA_Boolean", "UA_SByte", "UA_Byte", "UA_Int16", "UA_UInt16",
                   "UA_Int32", "UA_UInt32", "UA_Int64", "UA_UInt64", "UA_Float",
@@ -73,15 +73,15 @@ def createEnumerated(element):
         if child.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedValue":
             valuemap[name + "_" + child.get("Name")] = child.get("Value")
     valuemap = OrderedDict(sorted(valuemap.iteritems(), key=lambda (k,v): int(v)))
-    printh("typedef UA_UInt32 " + name + ";")
+    printh("typedef UA_Int32 " + name + ";")
     printh("enum " + name + "_enum { \n\t" +
            ",\n\t".join(map(lambda (key, value) : key.upper() + " = " + value, valuemap.iteritems())) +
            "\n};")
     printh("UA_TYPE_PROTOTYPES (" + name + ")")
     printh("UA_TYPE_BINARY_ENCODING(" + name + ")")
     printh("UA_TYPE_XML_ENCODING(" + name + ")\n")
-    printc("UA_TYPE_AS(" + name + ", UA_UInt32)")
-    printc("UA_TYPE_BINARY_ENCODING_AS(" + name + ", UA_UInt32)")
+    printc("UA_TYPE_AS(" + name + ", UA_Int32)")
+    printc("UA_TYPE_BINARY_ENCODING_AS(" + name + ", UA_Int32)")
     printc('''UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(%(name)s)
 UA_TYPE_METHOD_ENCODEXML_NOTIMPL(%(name)s)
 UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s\n)''')