diff --git a/CMakePresets.json b/CMakePresets.json index 80e41fd..090d347 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -18,7 +18,9 @@ "cacheVariables": { "CPP_STARTER_USE_SML": "ON", "CPP_STARTER_USE_BOOST_BEAST": "ON", - "CPP_STARTER_USE_CROW": "ON" + "CPP_STARTER_USE_CROW": "ON", + "CPP_STARTER_USE_OPEN62541PP": "ON", + "CPP_STARTER_USE_OPEN62541": "ON" } }, { diff --git a/cmake/Options.cmake b/cmake/Options.cmake index 466553a..a445b56 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -36,6 +36,8 @@ OPTION(CPP_STARTER_USE_CROW "Enable compilation of crow sample" OFF) OPTION(CPP_STARTER_USE_CPPZMQ_PROTO "Enable compilation of protobuf and cppzmq sample" OFF) OPTION(CPP_STARTER_USE_EMBEDDED_TOOLCHAIN "Enable compilation of an example cortex m4 project" OFF) OPTION(CPP_STARTER_USE_QT "Enable compilation of an example QT project" OFF) +OPTION(CPP_STARTER_USE_OPEN62541PP "Enable compilation of an example open62541pp wrapper project" OFF) +OPTION(CPP_STARTER_USE_OPEN62541 "Enable compilation of an example open62541 project" OFF) # test frameworks OPTION(CPP_STARTER_USE_CATCH2 "Enable compilation of an example test project using catch2" ON) diff --git a/conanfile.py b/conanfile.py index ea3dcea..a9f829f 100644 --- a/conanfile.py +++ b/conanfile.py @@ -7,7 +7,7 @@ class HelloConan(ConanFile): settings = 'os', 'compiler', 'build_type', 'arch' generators = 'CMakeDeps', 'CMakeToolchain' - default_options = {'fmt/*:header_only': True, 'spdlog/*:header_only': True, 'qt/*:with_fontconfig': False} + default_options = {'fmt/*:header_only': True, 'spdlog/*:header_only': True, 'qt/*:with_fontconfig': False, 'open62541/*:cpp_compatible': True} def requirements(self): if self.settings.get_safe('arch') == 'armv7': @@ -22,12 +22,14 @@ def requirements(self): if os.getenv("CONFIGURE_QT") == '1': self.requires('qt/6.7.3') else: + self.requires('sml/1.1.11') self.requires('nlohmann_json/3.11.3') self.requires('boost/1.87.0') self.requires('crowcpp-crow/1.2.0') self.requires('cppzmq/4.10.0') self.requires('protobuf/5.29.3') + self.requires('open62541/1.4.6') def configure(self): cmake = CMakeToolchain(self) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8d85756..f52873a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,18 @@ IF(CPP_STARTER_USE_QT) ADD_SUBDIRECTORY(qt) ENDIF() +# open62541pp example +IF(CPP_STARTER_USE_OPEN62541PP) + MESSAGE("Using open62541pp") + ADD_SUBDIRECTORY(open62541pp) +ENDIF() + +# open62541 example +IF(CPP_STARTER_USE_OPEN62541) + MESSAGE("Using open62541") + ADD_SUBDIRECTORY(open62541) +ENDIF() + FIND_PACKAGE(docopt REQUIRED) FIND_PACKAGE(spdlog REQUIRED) diff --git a/src/open62541/CMakeLists.txt b/src/open62541/CMakeLists.txt new file mode 100644 index 0000000..3e7163c --- /dev/null +++ b/src/open62541/CMakeLists.txt @@ -0,0 +1,5 @@ +ADD_EXECUTABLE(open62541_client client.cpp) +TARGET_LINK_LIBRARIES(open62541_client PRIVATE open62541::open62541) + +ADD_EXECUTABLE(open62541_server server.cpp) +TARGET_LINK_LIBRARIES(open62541_server PRIVATE open62541::open62541) diff --git a/src/open62541/client.cpp b/src/open62541/client.cpp new file mode 100644 index 0000000..df2f3d5 --- /dev/null +++ b/src/open62541/client.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +// This example is the original one from the open62541 examples + +int main() +{ + /* Create a client and connect */ + UA_Client *client = UA_Client_new(); + UA_ClientConfig_setDefault(UA_Client_getConfig(client)); + UA_StatusCode status = UA_Client_connect(client, "opc.tcp://localhost:4840"); + if (status != UA_STATUSCODE_GOOD) { + UA_Client_delete(client); + return status; + } + + /* Read the value attribute of the node. UA_Client_readValueAttribute is a + * wrapper for the raw read service available as UA_Client_Service_read. */ + UA_Variant value;/* Variants can hold scalar values and arrays of any type */ + UA_Variant_init(&value); + status = UA_Client_readValueAttribute(client, UA_NODEID_STRING(1, "the.answer"), &value); + if (status == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_INT32])) + { + printf("the value is: %i\n", *static_cast(value.data)); + } + + /* Clean up */ + UA_Variant_clear(&value); + UA_Client_delete(client);/* Disconnects the client internally */ + return status == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/open62541/server.cpp b/src/open62541/server.cpp new file mode 100644 index 0000000..54e05e8 --- /dev/null +++ b/src/open62541/server.cpp @@ -0,0 +1,19 @@ +#include +#include + +// This example is the original one from the open62541 examples + +int main() +{ + UA_Server *server = UA_Server_new(); + UA_Server_run_startup(server); + + /* Should the server networklayer block (with a timeout) until a message + arrives or should it return immediately? */ + const UA_Boolean waitInternal = true; + while (true) { UA_Server_run_iterate(server, waitInternal); } + + UA_Server_run_shutdown(server); + UA_Server_delete(server); + return 0; +} diff --git a/src/open62541pp/CMakeLists.txt b/src/open62541pp/CMakeLists.txt new file mode 100644 index 0000000..38f52f8 --- /dev/null +++ b/src/open62541pp/CMakeLists.txt @@ -0,0 +1,16 @@ +INCLUDE(FetchContent) + +FETCHCONTENT_DECLARE( + open62541pp + GIT_REPOSITORY https://github.com/open62541pp/open62541pp.git + GIT_TAG v0.15.0 +) +FETCHCONTENT_MAKEAVAILABLE(open62541pp) + +ADD_EXECUTABLE(open62541pp_client client.cpp) +TARGET_LINK_LIBRARIES(open62541pp_client PRIVATE open62541pp::open62541pp) +TARGET_INCLUDE_DIRECTORIES(open62541pp_client PUBLIC ${open62541pp_SOURCE_DIR}) + +ADD_EXECUTABLE(open62541pp_server server.cpp) +TARGET_LINK_LIBRARIES(open62541pp_server PRIVATE open62541pp::open62541pp) +TARGET_INCLUDE_DIRECTORIES(open62541pp_server PUBLIC ${open62541pp_SOURCE_DIR}) diff --git a/src/open62541pp/client.cpp b/src/open62541pp/client.cpp new file mode 100644 index 0000000..bb5af50 --- /dev/null +++ b/src/open62541pp/client.cpp @@ -0,0 +1,13 @@ +#include + +#include + +int main() { + opcua::Client client; + client.connect("opc.tcp://localhost:4840"); + + opcua::Node node = client.getNode(opcua::VariableId::Server_ServerStatus_CurrentTime); + const auto dt = node.readValueScalar(); + + std::cout << "Server date (UTC): " << dt.format("%Y-%m-%d %H:%M:%S") << "\n"; +} diff --git a/src/open62541pp/server.cpp b/src/open62541pp/server.cpp new file mode 100644 index 0000000..6c38bbf --- /dev/null +++ b/src/open62541pp/server.cpp @@ -0,0 +1,15 @@ +#include + +int main() { + opcua::Server server; + + // Add a variable node to the Objects node + opcua::Node parentNode = server.getObjectsNode(); + opcua::Node myIntegerNode = parentNode.addVariable({1, 1000}, "TheAnswer"); + // Write some node attributes + myIntegerNode.writeDisplayName({"en-US", "The Answer"}) + .writeDataType(opcua::DataTypeId::Int32) + .writeValueScalar(42); + + server.run(); +}