Browse Source

completing NS0 shim fixes #233, changed application name to "open62541"

Stasik0 10 years ago
parent
commit
6617707eab
1 changed files with 199 additions and 75 deletions
  1. 199 75
      src/server/ua_server.c

+ 199 - 75
src/server/ua_server.c

@@ -13,7 +13,7 @@ const UA_EXPORT UA_ServerConfig UA_ServerConfig_standard = {
         (char *[]){"password","password1"},
         2,
         "urn:unconfigured:open62541:open62541Server",
-        "Unconfigured open62541 application"
+        "open62541"
 };
 
 /**********************/
@@ -120,19 +120,28 @@ static void UA_Server_cleanup(UA_Server *server, void *nothing) {
     UA_SecureChannelManager_cleanupTimedOut(&server->secureChannelManager, now);
 }
 
+#define MANUFACRURER_NAME "open62541"
+#define PRODUCT_NAME "open62541 OPC UA Server"
+#define STRINGIFY(x) #x //some magic
+#define TOSTRING(x) STRINGIFY(x) //some magic
+#define SOFTWARE_VERSION TOSTRING(OPEN62541_VERSION_MAJOR) "." TOSTRING(OPEN62541_VERSION_MINOR) "." TOSTRING(OPEN62541_VERSION_PATCH)
+#define BUILD_NUMBER "0"
+
+static void getBulidInfo(const UA_Server* server, UA_BuildInfo *buildInfo){
+    buildInfo->productUri = UA_STRING_ALLOC(PRODUCT_URI);
+    buildInfo->manufacturerName = UA_STRING_ALLOC(MANUFACRURER_NAME);
+    buildInfo->productName = UA_STRING_ALLOC(PRODUCT_NAME);
+    buildInfo->softwareVersion = UA_STRING_ALLOC(SOFTWARE_VERSION);
+    buildInfo->buildNumber = UA_STRING_ALLOC(BUILD_NUMBER);
+    buildInfo->buildDate = server->buildDate;
+}
+
 static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
     UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
     status->startTime   = ((const UA_Server*)handle)->startTime;
     status->currentTime = UA_DateTime_now();
     status->state       = UA_SERVERSTATE_RUNNING;
-    status->buildInfo.productUri = UA_STRING("http://www.open62541.org");
-    status->buildInfo.manufacturerName = UA_STRING("open62541");
-    status->buildInfo.productName = UA_STRING("open62541 OPC UA Server");
-#define STRINGIFY(x) #x //some magic
-#define TOSTRING(x) STRINGIFY(x) //some magic
-    status->buildInfo.softwareVersion = UA_STRING(TOSTRING(OPEN62541_VERSION_MAJOR) "." TOSTRING(OPEN62541_VERSION_MINOR) "." TOSTRING(OPEN62541_VERSION_PATCH));
-    status->buildInfo.buildNumber = UA_STRING("0");
-    status->buildInfo.buildDate = ((const UA_Server*)handle)->buildDate;
+    getBulidInfo(((const UA_Server*)handle), &status->buildInfo);
     status->secondsTillShutdown = 0;
 
     value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE];
@@ -149,7 +158,7 @@ static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp, UA_Dat
 }
 
 static void releaseStatus(void *handle, UA_DataValue *value) {
-    UA_free(value->value.data);
+    UA_ServerStatusDataType_delete((UA_ServerStatusDataType*)value->value.data);
     value->value.data = UA_NULL;
     value->hasValue = UA_FALSE;
     UA_DataValue_deleteMembers(value);
@@ -680,6 +689,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     addObjectTypeNode(server, "ServerDiagnosticsType", UA_NS0ID_SERVERDIAGNOSTICSTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "ServerCapatilitiesType", UA_NS0ID_SERVERCAPABILITIESTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "ServerStatusType", UA_NS0ID_SERVERSTATUSTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
+    addObjectTypeNode(server, "BuildInfoType", UA_NS0ID_BUILDINFOTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
 
     /**************/
     /* Data Types */
@@ -724,6 +734,9 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     addDataTypeNode(server, "DiagnosticInfo", UA_NS0ID_DIAGNOSTICINFO, UA_NS0ID_BASEDATATYPE);
     addDataTypeNode(server, "Enumeration", UA_NS0ID_ENUMERATION, UA_NS0ID_BASEDATATYPE);
         addDataTypeNode(server, "ServerState", UA_NS0ID_SERVERSTATE, UA_NS0ID_ENUMERATION);
+    addDataTypeNode(server, "Structure", UA_NS0ID_STRUCTURE, UA_NS0ID_BASEDATATYPE);
+        addDataTypeNode(server, "ServerStatusDataType", UA_NS0ID_SERVERSTATUSDATATYPE, UA_NS0ID_STRUCTURE);
+        addDataTypeNode(server, "BuildInfo", UA_NS0ID_BUILDINFO, UA_NS0ID_STRUCTURE);
 
    UA_ObjectNode *variabletypes = UA_ObjectNode_new();
    copyNames((UA_Node*)variabletypes, "VariableTypes");
@@ -810,71 +823,182 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
            UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
    UA_VariableNode *maxBrowseContinuationPoints = UA_VariableNode_new();
-   copyNames((UA_Node*)maxBrowseContinuationPoints, "MaxBrowseContinuationPoints");
-   maxBrowseContinuationPoints->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS;
-   maxBrowseContinuationPoints->value.variant.data = UA_UInt16_new();
-   *((UA_UInt16*)maxBrowseContinuationPoints->value.variant.data) = 1;
-   maxBrowseContinuationPoints->value.variant.type = &UA_TYPES[UA_TYPES_UINT16];
-   UA_Server_addNode(server, (UA_Node*)maxBrowseContinuationPoints,
-       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
-       UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS),
-       UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
-
-   UA_ObjectNode *serverdiagnostics = UA_ObjectNode_new();
-   copyNames((UA_Node*)serverdiagnostics, "ServerDiagnostics");
-   serverdiagnostics->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS;
-   UA_Server_addNode(server, (UA_Node*)serverdiagnostics,
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-           UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE));
-
-   UA_VariableNode *enabledFlag = UA_VariableNode_new();
-   copyNames((UA_Node*)enabledFlag, "EnabledFlag");
-   enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;
-   enabledFlag->value.variant.data = UA_Boolean_new(); //itialized as false
-   enabledFlag->value.variant.type = &UA_TYPES[UA_TYPES_BOOLEAN];
-   enabledFlag->valueRank = 1;
-   enabledFlag->minimumSamplingInterval = 1.0;
-   enabledFlag->historizing = UA_FALSE;
-   UA_Server_addNode(server, (UA_Node*)enabledFlag,
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
-           UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
-
-   UA_VariableNode *serverstatus = UA_VariableNode_new();
-   copyNames((UA_Node*)serverstatus, "ServerStatus");
-   serverstatus->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS);
-   serverstatus->valueSource = UA_VALUESOURCE_DATASOURCE;
-   serverstatus->value.dataSource = (UA_DataSource) {.handle = server, .read = readStatus,
-       .release = releaseStatus, .write = UA_NULL};
-   UA_Server_addNode(server, (UA_Node*)serverstatus, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-           UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSTYPE));
-
-   UA_VariableNode *state = UA_VariableNode_new();
-   UA_ServerState *stateEnum = UA_ServerState_new();
-   *stateEnum = UA_SERVERSTATE_RUNNING;
-   copyNames((UA_Node*)state, "State");
-   state->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERSTATUS_STATE;
-   state->value.variant.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
-   state->value.variant.arrayLength = -1;
-   state->value.variant.data = stateEnum; // points into the other object.
-   UA_NodeStore_insert(server->nodestore, (UA_Node*)state, UA_NULL);
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE));
-
-   UA_VariableNode *currenttime = UA_VariableNode_new();
-   copyNames((UA_Node*)currenttime, "CurrentTime");
-   currenttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
-   currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
-   currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime,
-       .release = releaseCurrentTime, .write = UA_NULL};
-   UA_Server_addNode(server, (UA_Node*)currenttime, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
-           UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+    copyNames((UA_Node*)maxBrowseContinuationPoints, "MaxBrowseContinuationPoints");
+    maxBrowseContinuationPoints->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS;
+    maxBrowseContinuationPoints->value.variant.data = UA_UInt16_new();
+    *((UA_UInt16*)maxBrowseContinuationPoints->value.variant.data) = 1;
+    maxBrowseContinuationPoints->value.variant.type = &UA_TYPES[UA_TYPES_UINT16];
+    UA_Server_addNode(server, (UA_Node*)maxBrowseContinuationPoints,
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
+
+    UA_ObjectNode *serverdiagnostics = UA_ObjectNode_new();
+    copyNames((UA_Node*)serverdiagnostics, "ServerDiagnostics");
+    serverdiagnostics->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS;
+    UA_Server_addNode(server, (UA_Node*)serverdiagnostics,
+            UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
+            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE));
+
+    UA_VariableNode *enabledFlag = UA_VariableNode_new();
+     copyNames((UA_Node*)enabledFlag, "EnabledFlag");
+     enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;
+     enabledFlag->value.variant.data = UA_Boolean_new(); //itialized as false
+     enabledFlag->value.variant.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+     enabledFlag->valueRank = 1;
+     enabledFlag->minimumSamplingInterval = 1.0;
+     enabledFlag->historizing = UA_FALSE;
+     UA_Server_addNode(server, (UA_Node*)enabledFlag,
+             UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
+             UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+     ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+             UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
+
+     UA_VariableNode *serverstatus = UA_VariableNode_new();
+      copyNames((UA_Node*)serverstatus, "ServerStatus");
+      serverstatus->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS);
+      serverstatus->valueSource = UA_VALUESOURCE_DATASOURCE;
+      serverstatus->value.dataSource = (UA_DataSource) {.handle = server, .read = readStatus,
+          .release = releaseStatus, .write = UA_NULL};
+      UA_Server_addNode(server, (UA_Node*)serverstatus, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
+              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+      ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSTYPE));
+
+      UA_VariableNode *starttime = UA_VariableNode_new();
+      copyNames((UA_Node*)starttime, "StartTime");
+      starttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME);
+      starttime->value.variant.storageType = UA_VARIANT_DATA_NODELETE;
+      starttime->value.variant.data = &server->startTime;
+      starttime->value.variant.type = &UA_TYPES[UA_TYPES_DATETIME];
+      UA_Server_addNode(server, (UA_Node*)starttime, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+      ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+      UA_VariableNode *currenttime = UA_VariableNode_new();
+      copyNames((UA_Node*)currenttime, "CurrentTime");
+      currenttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
+      currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
+      currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime,
+          .release = releaseCurrentTime, .write = UA_NULL};
+      UA_Server_addNode(server, (UA_Node*)currenttime, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+      ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+      UA_VariableNode *state = UA_VariableNode_new();
+      UA_ServerState *stateEnum = UA_ServerState_new();
+      *stateEnum = UA_SERVERSTATE_RUNNING;
+      copyNames((UA_Node*)state, "State");
+      state->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERSTATUS_STATE;
+      state->value.variant.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
+      state->value.variant.arrayLength = -1;
+      state->value.variant.data = stateEnum; // points into the other object.
+      UA_Server_addNode(server, (UA_Node*)state, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+      ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+      UA_VariableNode *buildinfo = UA_VariableNode_new();
+       copyNames((UA_Node*)buildinfo, "BuildInfo");
+       buildinfo->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO);
+       buildinfo->value.variant.data = UA_BuildInfo_new();
+       buildinfo->value.variant.type = &UA_TYPES[UA_TYPES_BUILDINFO];
+       getBulidInfo(server, (UA_BuildInfo*)buildinfo->value.variant.data);
+       UA_Server_addNode(server, (UA_Node*)buildinfo, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BUILDINFOTYPE));
+
+       UA_VariableNode *producturi = UA_VariableNode_new();
+       copyNames((UA_Node*)producturi, "ProductUri");
+       producturi->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI);
+       producturi->value.variant.data = UA_String_new();
+       *((UA_String*)producturi->value.variant.data) = UA_STRING_ALLOC(PRODUCT_URI);
+       producturi->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)producturi, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *manufacturername = UA_VariableNode_new();
+       copyNames((UA_Node*)manufacturername, "ManufacturererName");
+       manufacturername->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME);
+       manufacturername->value.variant.data = UA_String_new();
+       *((UA_String*)manufacturername->value.variant.data) = UA_STRING_ALLOC(MANUFACRURER_NAME);
+       manufacturername->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)manufacturername, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *productname = UA_VariableNode_new();
+       copyNames((UA_Node*)productname, "ProductName");
+       productname->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME);
+       productname->value.variant.data = UA_String_new();
+       *((UA_String*)productname->value.variant.data) = UA_STRING_ALLOC(PRODUCT_NAME);
+       productname->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)productname, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *softwareversion = UA_VariableNode_new();
+       copyNames((UA_Node*)softwareversion, "SoftwareName");
+       softwareversion->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION);
+       softwareversion->value.variant.data = UA_String_new();
+       *((UA_String*)softwareversion->value.variant.data) = UA_STRING_ALLOC(SOFTWARE_VERSION);
+       softwareversion->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)softwareversion, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *buildnumber = UA_VariableNode_new();
+       copyNames((UA_Node*)buildnumber, "BuildNumber");
+       buildnumber->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER);
+       buildnumber->value.variant.data = UA_String_new();
+       *((UA_String*)buildnumber->value.variant.data) = UA_STRING_ALLOC(BUILD_NUMBER);
+       buildnumber->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)buildnumber, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *builddate = UA_VariableNode_new();
+       copyNames((UA_Node*)builddate, "BuildDate");
+       builddate->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE);
+       builddate->value.variant.storageType = UA_VARIANT_DATA_NODELETE;
+       builddate->value.variant.data = &server->buildDate;
+       builddate->value.variant.type = &UA_TYPES[UA_TYPES_DATETIME];
+       UA_Server_addNode(server, (UA_Node*)builddate, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *secondstillshutdown = UA_VariableNode_new();
+       copyNames((UA_Node*)secondstillshutdown, "SecondsTillShutdown");
+       secondstillshutdown->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN);
+       secondstillshutdown->value.variant.data = UA_UInt32_new();
+       secondstillshutdown->value.variant.type = &UA_TYPES[UA_TYPES_UINT32];
+       UA_Server_addNode(server, (UA_Node*)secondstillshutdown, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *shutdownreason = UA_VariableNode_new();
+       copyNames((UA_Node*)shutdownreason, "ShutdownReason");
+       shutdownreason->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON);
+       shutdownreason->value.variant.data = UA_LocalizedText_new();
+       shutdownreason->value.variant.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+       UA_Server_addNode(server, (UA_Node*)shutdownreason, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
 #ifdef DEMO_NODESET