Browse Source

let users add their custom namespaces

Julius Pfrommer 10 years ago
parent
commit
6423239137

+ 3 - 2
README.md

@@ -49,13 +49,14 @@ int main(int argc, char** argv) {
     UA_Server_addNetworkLayer(server,
         ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, PORT));
     UA_Server_setLogger(server, Logger_Stdout_new());
+    UA_UInt16 nsIndex = UA_Server_addNamespace(server, "myApplicationNamespace");
 
     /* add a variable node */
     UA_Variant *myIntegerVariant = UA_Variant_new();
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(nsIndex, "the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(nsIndex, "the.answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,

+ 10 - 9
examples/server.c

@@ -32,7 +32,7 @@ UA_Logger logger;
 /*************************/
 /* Read-only data source */
 /*************************/
-static UA_StatusCode readTimeData(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
 	UA_DateTime *currentTime = UA_DateTime_new();
 	if(!currentTime)
 		return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -50,7 +50,7 @@ static UA_StatusCode readTimeData(const void *handle, UA_Boolean sourceTimeStamp
 	return UA_STATUSCODE_GOOD;
 }
 
-static void releaseTimeData(const void *handle, UA_DataValue *value) {
+static void releaseTimeData(void *handle, UA_DataValue *value) {
 	UA_DateTime_delete((UA_DateTime*)value->value.data);
 }
 
@@ -59,7 +59,7 @@ static void releaseTimeData(const void *handle, UA_DataValue *value) {
 /*      Only on Linux        */
 /*****************************/
 FILE* temperatureFile = NULL;
-static UA_StatusCode readTemperature(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
 	UA_Double* currentTemperature = UA_Double_new();
 
 	if(!currentTemperature)
@@ -83,7 +83,7 @@ static UA_StatusCode readTemperature(const void *handle, UA_Boolean sourceTimeSt
 	return UA_STATUSCODE_GOOD;
 }
 
-static void releaseTemperature(const void *handle, UA_DataValue *value) {
+static void releaseTemperature(void *handle, UA_DataValue *value) {
 	UA_Double_delete((UA_Double*)value->value.data);
 }
 
@@ -97,7 +97,7 @@ FILE* triggerFile = NULL;
 FILE* ledFile = NULL;
 UA_Boolean ledStatus = 0;
 
-static UA_StatusCode readLedStatus(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
 	/* In order to reduce blocking time, we could alloc memory for every read
        and return a copy of the data. */
 #ifdef UA_MULTITHREADING
@@ -116,7 +116,7 @@ static UA_StatusCode readLedStatus(const void *handle, UA_Boolean sourceTimeStam
 	return UA_STATUSCODE_GOOD;
 }
 
-static void releaseLedStatus(const void *handle, UA_DataValue *value) {
+static void releaseLedStatus(void *handle, UA_DataValue *value) {
 	/* If we allocated memory for a specific read, free the content of the
        variantdata. */
 	value->value.arrayLength = -1;
@@ -126,7 +126,7 @@ static void releaseLedStatus(const void *handle, UA_DataValue *value) {
 #endif
 }
 
-static UA_StatusCode writeLedStatus(const void *handle, const UA_Variant *data) {
+static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data) {
 #ifdef UA_MULTITHREADING
 	pthread_rwlock_wrlock(&writeLock);
 #endif
@@ -197,6 +197,7 @@ int main(int argc, char** argv) {
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
 	UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+    UA_UInt16 nsIndex = UA_Server_addNamespace(server, "myApplicationNamespace");
 
 	// print the status every 2 sec
 	UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
@@ -209,7 +210,7 @@ int main(int argc, char** argv) {
 		.read = readTimeData,
 		.release = releaseTimeData,
 		.write = NULL};
-	const UA_QualifiedName dateName = UA_QUALIFIEDNAME(0, "current time");
+	const UA_QualifiedName dateName = UA_QUALIFIEDNAME(nsIndex, "current time");
 	UA_Server_addDataSourceVariableNode(server, dateDataSource, dateName, UA_NODEID_NULL,
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
@@ -223,7 +224,7 @@ int main(int argc, char** argv) {
 			.read = readTemperature,
 			.release = releaseTemperature,
 			.write = NULL};
-		const UA_QualifiedName ledName = UA_QUALIFIEDNAME(0, "cpu temperature");
+		const UA_QualifiedName ledName = UA_QUALIFIEDNAME(nsIndex, "cpu temperature");
 		UA_Server_addDataSourceVariableNode(server, temperatureDataSource, ledName, UA_NODEID_NULL, 
                                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                             UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));

+ 1 - 0
examples/server.cpp

@@ -26,6 +26,7 @@ int main()
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 
 	// add a variable node to the adresspace
+    UA_Server_addNamespace(server, "myApplicationNamespace");
     UA_Variant *myIntegerVariant = UA_Variant_new();
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);

+ 1 - 0
examples/server_simple.c

@@ -66,6 +66,7 @@ int main(int argc, char** argv) {
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+    UA_Server_addNamespace(server, "myApplicationNamespace");
 
     UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL, .work.methodCall = {.method = testCallback, .data = NULL} };
     UA_Server_addRepeatedWorkItem(server, &work, 20000000, NULL); // call every 2 sec

+ 1 - 0
examples/server_udp.c

@@ -28,6 +28,7 @@ int main(int argc, char** argv) {
 
 	UA_Server *server = UA_Server_new();
     UA_Server_addNetworkLayer(server, ServerNetworkLayerUDP_new(UA_ConnectionConfig_standard, 16664));
+    UA_Server_addNamespace(server, "myApplicationNamespace");
 
 	// add a variable node to the adresspace
     UA_Variant *myIntegerVariant = UA_Variant_new();

+ 11 - 8
include/ua_server.h

@@ -70,12 +70,15 @@ UA_StatusCode UA_EXPORT UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_
  * callback can be set to null.
  **/
 typedef struct {
-    const void *handle;
-    UA_StatusCode (*read)(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value);
-    void (*release)(const void *handle, UA_DataValue *value);
-    UA_StatusCode (*write)(const void *handle, const UA_Variant *data);
+    void *handle;
+    UA_StatusCode (*read)(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value);
+    void (*release)(void *handle, UA_DataValue *value);
+    UA_StatusCode (*write)(void *handle, const UA_Variant *data);
 } UA_DataSource;
 
+/** @brief Add a new namespace to the server. Returns the index of the new namespace */
+UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
+
 /** Add a reference to the server's address space */
 UA_StatusCode UA_EXPORT UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item);
 
@@ -213,10 +216,6 @@ void UA_EXPORT UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLaye
 
 /** @} */
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
 #ifndef __cplusplus /* the external nodestore does not work with c++ so far */
 
 /**
@@ -291,4 +290,8 @@ typedef struct UA_ExternalNodeStore {
 
 #endif /* external nodestore */
 
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
 #endif /* UA_SERVER_H_ */

+ 53 - 22
src/server/ua_server.c

@@ -27,7 +27,8 @@ static void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens) {
 /*****************/
 
 void UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer networkLayer) {
-    UA_ServerNetworkLayer *newlayers = UA_realloc(server->nls, sizeof(UA_ServerNetworkLayer)*(server->nlsSize+1));
+    UA_ServerNetworkLayer *newlayers =
+        UA_realloc(server->nls, sizeof(UA_ServerNetworkLayer)*(server->nlsSize+1));
     if(!newlayers) {
         UA_LOG_ERROR(server->logger, UA_LOGGERCATEGORY_SERVER, "Networklayer added");
         return;
@@ -53,7 +54,7 @@ void UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer networkL
 }
 
 void UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate) {
-    for(UA_Int32 i=0;i<server->endpointDescriptionsSize;i++)
+    for(UA_Int32 i = 0; i < server->endpointDescriptionsSize; i++)
         UA_ByteString_copy(&certificate, &server->endpointDescriptions[i].serverCertificate);
 }
 
@@ -61,12 +62,21 @@ void UA_Server_setLogger(UA_Server *server, UA_Logger logger) {
     server->logger = logger;
 }
 
+UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) {
+    server->namespaces = UA_realloc(server->namespaces, sizeof(UA_String) * (server->namespacesSize+1));
+    server->namespaces[server->namespacesSize] = UA_STRING_ALLOC(name);
+    server->namespacesSize++;
+    return server->namespacesSize-1;
+}
+
 /**********/
 /* Server */
 /**********/
 
+/* The server needs to be stopped before it can be deleted */
 void UA_Server_delete(UA_Server *server) {
-	// The server needs to be stopped before it can be deleted
+	// Delete the timed work
+	UA_Server_deleteTimedWork(server);
 
 	// Delete all internal data
 	UA_ApplicationDescription_deleteMembers(&server->description);
@@ -74,14 +84,12 @@ void UA_Server_delete(UA_Server *server) {
 	UA_SessionManager_deleteMembers(&server->sessionManager);
 	UA_NodeStore_delete(server->nodestore);
 	UA_ByteString_deleteMembers(&server->serverCertificate);
-	UA_Array_delete(server->endpointDescriptions, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION], server->endpointDescriptionsSize);
-
-	// Delete the timed work
-	UA_Server_deleteTimedWork(server);
-
+    UA_Array_delete(server->namespaces, &UA_TYPES[UA_TYPES_STRING], server->namespacesSize);
+	UA_Array_delete(server->endpointDescriptions, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION],
+                    server->endpointDescriptionsSize);
 
 	// Delete the network layers
-	for(UA_Int32 i=0;i<server->nlsSize;i++) {
+	for(UA_Int32 i = 0; i < server->nlsSize; i++) {
 		server->nls[i].free(server->nls[i].nlHandle);
 	}
 	UA_free(server->nls);
@@ -93,7 +101,7 @@ void UA_Server_delete(UA_Server *server) {
 	UA_free(server);
 }
 
-static UA_StatusCode readStatus(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+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();
@@ -120,11 +128,28 @@ static UA_StatusCode readStatus(const void *handle, UA_Boolean sourceTimeStamp,
     return UA_STATUSCODE_GOOD;
 }
 
-static void releaseStatus(const void *handle, UA_DataValue *value) {
+static void releaseStatus(void *handle, UA_DataValue *value) {
     UA_DataValue_deleteMembers(value);
 }
 
-static UA_StatusCode readCurrentTime(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readNamespaces(void *handle, UA_Boolean sourceTimestamp, UA_DataValue *value) {
+    UA_Server *server = (UA_Server*)handle;
+    value->hasValue = UA_TRUE;
+    value->value.storageType = UA_VARIANT_DATA_NODELETE;
+    value->value.type = &UA_TYPES[UA_TYPES_STRING];
+    value->value.arrayLength = server->namespacesSize;
+    value->value.data = server->namespaces;
+    if(sourceTimestamp) {
+        value->hasSourceTimestamp = UA_TRUE;
+        value->sourceTimestamp = UA_DateTime_now();
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
+static void releaseNamespaces(void *handle, UA_DataValue *value) {
+}
+
+static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
 	UA_DateTime *currentTime = UA_DateTime_new();
 	if(!currentTime)
 		return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -142,7 +167,7 @@ static UA_StatusCode readCurrentTime(const void *handle, UA_Boolean sourceTimeSt
 	return UA_STATUSCODE_GOOD;
 }
 
-static void releaseCurrentTime(const void *handle, UA_DataValue *value) {
+static void releaseCurrentTime(void *handle, UA_DataValue *value) {
 	UA_DateTime_delete((UA_DateTime*)value->value.data);
 }
 
@@ -161,7 +186,10 @@ static void addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid,
                       &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 }
 
-static UA_VariableTypeNode* createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid, UA_Int32 parent, UA_Boolean abstract) {
+static UA_VariableTypeNode*
+createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
+                       UA_Int32 parent, UA_Boolean abstract)
+{
     UA_VariableTypeNode *variabletype = UA_VariableTypeNode_new();
     copyNames((UA_Node*)variabletype, name);
     variabletype->nodeId.identifier.numeric = variabletypeid;
@@ -170,7 +198,8 @@ static UA_VariableTypeNode* createVariableTypeNode(UA_Server *server, char* name
     return variabletype;
 }
 
-static void addVariableTypeNode_organized(UA_Server *server, char* name, UA_UInt32 variabletypeid, UA_Int32 parent, UA_Boolean abstract) {
+static void addVariableTypeNode_organized(UA_Server *server, char* name, UA_UInt32 variabletypeid,
+                                          UA_Int32 parent, UA_Boolean abstract) {
 	UA_VariableTypeNode *variabletype = createVariableTypeNode(server, name, variabletypeid, parent, abstract);
 
     UA_Server_addNode(server, (UA_Node*)variabletype,
@@ -178,7 +207,8 @@ static void addVariableTypeNode_organized(UA_Server *server, char* name, UA_UInt
                       &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 }
 
-static void addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32 variabletypeid, UA_Int32 parent, UA_Boolean abstract) {
+static void addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32 variabletypeid,
+                                        UA_Int32 parent, UA_Boolean abstract) {
 	UA_VariableTypeNode *variabletype = createVariableTypeNode(server, name, variabletypeid, parent, abstract);
 
     UA_Server_addNode(server, (UA_Node*)variabletype,
@@ -223,6 +253,10 @@ UA_Server * UA_Server_new(void) {
     server->externalNamespacesSize = 0;
     server->externalNamespaces = UA_NULL;
 
+    server->namespaces = UA_String_new();
+    *server->namespaces = UA_STRING_ALLOC("http://opcfoundation.org/UA/");
+    server->namespacesSize = 1;
+
     server->endpointDescriptions = UA_NULL;
     server->endpointDescriptionsSize = 0;
 
@@ -629,12 +663,9 @@ UA_Server * UA_Server_new(void) {
    UA_VariableNode *namespaceArray = UA_VariableNode_new();
    copyNames((UA_Node*)namespaceArray, "NamespaceArray");
    namespaceArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_NAMESPACEARRAY;
-   namespaceArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 2);
-   namespaceArray->value.variant.arrayLength = 2;
-   namespaceArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
-   // Fixme: Insert the external namespaces
-   ((UA_String *)namespaceArray->value.variant.data)[0] = UA_STRING_ALLOC("http://opcfoundation.org/UA/");
-   ((UA_String *)namespaceArray->value.variant.data)[1] = UA_STRING_ALLOC(APPLICATION_URI);
+   namespaceArray->valueSource = UA_VALUESOURCE_DATASOURCE;
+   namespaceArray->value.dataSource = (UA_DataSource) {.handle = server, .read = readNamespaces,
+	   .release = releaseNamespaces, .write = UA_NULL};
    namespaceArray->valueRank = 1;
    namespaceArray->minimumSamplingInterval = 1.0;
    namespaceArray->historizing = UA_FALSE;

+ 5 - 0
src/server/ua_server_addressspace.c

@@ -204,6 +204,11 @@ UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *no
     UA_AddNodesResult result;
     UA_AddNodesResult_init(&result);
 
+    if(node->nodeId.namespaceIndex >= server->namespacesSize) {
+        result.statusCode = UA_STATUSCODE_BADNODEIDINVALID;
+        return result;
+    }
+
     const UA_Node *parent = UA_NodeStore_get(server->nodestore, &parentNodeId->nodeId);
     if(!parent) {
         result.statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID;

+ 2 - 0
src/server/ua_server_internal.h

@@ -33,6 +33,8 @@ struct UA_Server {
     UA_Logger logger;
 
     UA_NodeStore *nodestore;
+    UA_Int32 namespacesSize;
+    UA_String *namespaces;
     UA_Int32 externalNamespacesSize;
     UA_ExternalNamespace *externalNamespaces;
 

+ 3 - 3
src/server/ua_services_discovery.c

@@ -25,9 +25,9 @@ void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *reque
     for(UA_Int32 j = 0; j < server->endpointDescriptionsSize; j++) {
         relevant_endpoints[j] = UA_FALSE;
         if(request->profileUrisSize <= 0) {
-                relevant_endpoints[j] = UA_TRUE;
-                relevant_count++;
-                continue;
+            relevant_endpoints[j] = UA_TRUE;
+            relevant_count++;
+            continue;
         }
         for(UA_Int32 i = 0; i < request->profileUrisSize; i++) {
             if(UA_String_equal(&request->profileUris[i], &server->endpointDescriptions->transportProfileUri)) {