From 6ccb15f364eb16a7dee74b4c702c9dcca380d5cd Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Thu, 22 Jun 2023 00:15:21 +0100 Subject: [PATCH] Initial version of WDF OPC service --- .gitignore | 3 + .gitmodules | 6 ++ .vscode/c_cpp_properties.json | 23 +++++++ CMakeLists.txt | 27 ++++++++ open62541 | 1 + server.c | 126 ++++++++++++++++++++++++++++++++++ wdf-linux | 1 + 7 files changed, 187 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .vscode/c_cpp_properties.json create mode 100644 CMakeLists.txt create mode 160000 open62541 create mode 100644 server.c create mode 160000 wdf-linux diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77b8785 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +open62541/ +.vscode/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7b02134 --- /dev/null +++ b/.gitmodules @@ -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 index 0000000..3664dd8 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -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 index 0000000..e92203e --- /dev/null +++ b/CMakeLists.txt @@ -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 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 index 0000000..5220b19 --- /dev/null +++ b/server.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include + +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 index 0000000..4581e0f --- /dev/null +++ b/wdf-linux @@ -0,0 +1 @@ +Subproject commit 4581e0fb267ccb22d5983c69d00ff1e5be008897 -- 2.23.0