Initial version of WDF OPC service
authorPat Thoyts <pat.thoyts@gmail.com>
Wed, 21 Jun 2023 23:15:21 +0000 (00:15 +0100)
committerPat Thoyts <pat.thoyts@gmail.com>
Wed, 21 Jun 2023 23:15:21 +0000 (00:15 +0100)
.gitignore [new file with mode: 0644]
.gitmodules [new file with mode: 0644]
.vscode/c_cpp_properties.json [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
open62541 [new submodule]
server.c [new file with mode: 0644]
wdf-linux [new submodule]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..77b8785
--- /dev/null
@@ -0,0 +1,3 @@
+build/
+open62541/
+.vscode/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..7b02134
--- /dev/null
@@ -0,0 +1,6 @@
+[submodule "open62541"]
+       path = open62541
+       url = https://github.com/open62541/open62541.git
+[submodule "wdf-linux"]
+       path = wdf-linux
+       url = https://git.privyetmir.co.uk/wdf-linux
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644 (file)
index 0000000..3664dd8
--- /dev/null
@@ -0,0 +1,23 @@
+{
+    "configurations": [
+        {
+            "name": "Linux",
+            "compileCommands": "${workspaceFolder}/build/compile_commands.json",
+            "includePath": [
+                "${default}",
+                "open62541/include",
+                "open62541/arch",
+                "open62541/deps",
+                "open62541/plugins/include",
+                "build/open62541/src_generated",
+                "wdf-linux/wdflib"
+            ],
+            "defines": [],
+            "compilerPath": "/usr/bin/clang",
+            "cStandard": "c17",
+            "cppStandard": "c++14",
+            "intelliSenseMode": "linux-clang-x64"
+        }
+    ],
+    "version": 4
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e92203e
--- /dev/null
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.12)
+
+project(opc_server
+    VERSION 1.0
+    DESCRIPTION "Test out an OPC-UA server"
+    LANGUAGES C CXX
+)
+
+set (TARGET ${PROJECT_NAME})
+
+add_subdirectory(wdf-linux/wdflib wdflib)
+
+option (MY_INTERNAL_OPEN62541 "Use internal open62541 library" ON)
+if (MY_INTERNAL_OPEN62541)
+    add_subdirectory(open62541)  # target open62541::open62541
+else()
+    set (CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)  # prefer open62541's own config file
+    find_package(open62541 REQUIRED
+        COMPONENTS Events FullNamespace
+        PATHS /opt/open62541
+    )
+endif()
+
+add_executable(${TARGET} server.c)
+target_compile_features(${TARGET} PUBLIC c_std_11)
+target_include_directories(${TARGET} PUBLIC open62541::open62541 ${wdflib_SOURCE_DIR})
+target_link_libraries(${TARGET} PUBLIC open62541::open62541 wdflib)
diff --git a/open62541 b/open62541
new file mode 160000 (submodule)
index 0000000..1b7e2b5
--- /dev/null
+++ b/open62541
@@ -0,0 +1 @@
+Subproject commit 1b7e2b5d669661c568fda5577ee92011a029cf75
diff --git a/server.c b/server.c
new file mode 100644 (file)
index 0000000..5220b19
--- /dev/null
+++ b/server.c
@@ -0,0 +1,126 @@
+#include <open62541/plugin/log_stdout.h>
+#include <open62541/server.h>
+#include <open62541/server_config_default.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <wdf.h>
+
+typedef struct {
+    UA_Server *server;
+    UA_Int16 ns;
+    UA_NodeId renishawNode;
+} App;
+
+static volatile UA_Boolean is_running = true;
+
+static void on_interrupt(int signo)
+{
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
+    is_running = false;
+}
+
+static int usage(int exitCode)
+{
+    fprintf(stderr, "usage: opc_server ?-port NUM? FILENAME\n");
+    exit(exitCode);
+}
+
+static UA_StatusCode add_float(App *app, const char *name, const char *displayName, UA_Float value)
+{
+    UA_VariableAttributes attr = UA_VariableAttributes_default;
+    if (displayName != NULL)
+        attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", displayName);
+    UA_Variant_setScalar(&attr.value, &value, &UA_TYPES[UA_TYPES_FLOAT]);
+
+    char id[64] = {0};
+    strcat(id, "renishaw.spd.");
+    strcat(id, name);
+
+    UA_NodeId nodeId = UA_NODEID_STRING(app->ns, id);
+    UA_NodeId parentId = app->renishawNode;
+    UA_NodeId parentRefId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_NodeId varType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
+    UA_QualifiedName browseName = UA_QUALIFIEDNAME_ALLOC(app->ns, name);
+
+    return UA_Server_addVariableNode(
+        app->server, nodeId, parentId, parentRefId, browseName,
+        varType, attr, NULL, NULL);
+}
+
+static UA_StatusCode add_int32(App *app, const char *name, const char *displayName, UA_Int32 value)
+{
+    UA_VariableAttributes attr = UA_VariableAttributes_default;
+    if (displayName != NULL)
+        attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", displayName);
+    UA_Variant_setScalar(&attr.value, &value, &UA_TYPES[UA_TYPES_INT32]);
+
+    char id[64] = {0};
+    strcat(id, "renishaw.spd.");
+    strcat(id, name);
+
+    UA_NodeId nodeId = UA_NODEID_STRING_ALLOC(app->ns, id);
+    UA_NodeId parentId = app->renishawNode;
+    UA_NodeId parentRefId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_NodeId varType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
+    UA_QualifiedName browseName = UA_QUALIFIEDNAME_ALLOC(app->ns, name);
+
+    return UA_Server_addVariableNode(
+        app->server, nodeId, parentId, parentRefId, browseName,
+        varType, attr, NULL, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+    unsigned short port = 4840;
+    const char *filename = argv[argc - 1];
+
+    if (argc == 4 && strcmp("-port", argv[1]) == 0)
+        port = (unsigned short)strtoul(argv[2], NULL, 0);
+    else if (argc != 2)
+        usage(1);
+    if (filename[0] == '-')
+        usage(1);
+
+    signal(SIGINT, on_interrupt);
+    signal(SIGTERM, on_interrupt);
+
+    App application = {0};
+    App *app = &application;
+
+    WdfHeader wdf = {0};
+    FILE *fp = fopen(filename, "rb");
+    if (fp != NULL)
+        fread(&wdf, sizeof(WdfHeader), 1, fp);
+    else
+        fprintf(stderr, "failed to read wdf file\n");
+    fclose(fp);
+
+    app->server = UA_Server_new();
+    UA_ServerConfig *config = UA_Server_getConfig(app->server);
+    UA_StatusCode status = UA_ServerConfig_setMinimal(config, port, NULL);
+    app->ns = UA_Server_addNamespace(app->server, "renishaw.spd");
+
+    // Add new object node for our data as Renishaw
+    UA_ObjectAttributes rAttr = UA_ObjectAttributes_default;
+    status = UA_Server_addObjectNode(app->server, UA_NODEID_STRING(app->ns, "renishaw"),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        UA_QUALIFIEDNAME(app->ns, "Renishaw"),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
+        rAttr, NULL, &app->renishawNode);
+
+    if (UA_StatusCode_isGood(status))
+        status = add_int32(app, "nspectra", "Capacity", wdf.nspectra);
+    if (UA_StatusCode_isGood(status))
+        status = add_int32(app, "ncollected", "Count", wdf.ncollected);
+    if (UA_StatusCode_isGood(status))
+        status = add_int32(app, "npoints", "Points", wdf.npoints);
+    if (UA_StatusCode_isGood(status))
+        status = add_float(app, "laserwavenum", "Laser Wavenumber", wdf.laserwavenum);
+    if (UA_StatusCode_isGood(status))
+        status = UA_Server_run(app->server, &is_running);
+    
+    UA_Server_delete(app->server);
+    return UA_StatusCode_isGood(status) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
\ No newline at end of file
diff --git a/wdf-linux b/wdf-linux
new file mode 160000 (submodule)
index 0000000..4581e0f
--- /dev/null
+++ b/wdf-linux
@@ -0,0 +1 @@
+Subproject commit 4581e0fb267ccb22d5983c69d00ff1e5be008897