open62541++ is a C++ wrapper built on top of the amazing open62541 OPC UA (OPC Unified Architecture) library.
Features and goals:
- High-level and easy to use classes similar to the python-opcua API:
opcua::Server
opcua::Client
opcua::Node
- Safe wrapper classes for open62541
UA_*
types to prevent memory leaks - Native open62541 objects can be accessed using the
handle()
method of the wrapping classes - Extensible type conversion system to convert arbitrary types to/from native
UA_*
types - Cross-platform (tested on Windows, Linux and MacOS)
- Compatible with all stable open62541 versions (> v1.0)
- Easy installation and integration with CMake
- Use modern C++ (C++ 17) and best practices
- Less hurdle to get started with OPC UA
#include "open62541pp/open62541pp.h"
int main() {
opcua::Server server;
// add variable node
auto parentNode = server.getObjectsNode();
auto myIntegerNode = parentNode.addVariable({1, "the.answer"}, "the answer");
// set node attributes
myIntegerNode.writeDataType(opcua::Type::Int32);
myIntegerNode.writeDisplayName({"en-US", "the answer"});
myIntegerNode.writeDescription({"en-US", "the answer"});
myIntegerNode.writeScalar(42);
server.run();
}
#include <iostream>
#include "open62541pp/open62541pp.h"
int main() {
opcua::Client client;
client.connect("opc.tcp://localhost:4840");
auto node = client.getNode({0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME});
const auto dt = node.readScalar<opcua::DateTime>();
std::cout << "Server date (UTC): " << dt.format("%Y-%m-%d %H:%M:%S") << std::endl;
}
Type conversion from and to native UA_*
types are handled by the opcua::TypeConverter
struct.
Compile-time checks are used where possible:
opcua::Variant var;
// will compile
int number = 5;
var.setScalar(number);
var.setArrayCopy<double>({1.1, 2.2, 3.3});
// won't compile, because the std::string can't be assigned without copy (conversion needed)
std::string str{"test"};
var.setScalar(str);
// won't compile, because the type std::string is associated with more than one variant types:
// - opcua::Type::String
// - opcua::Type::ByteString
// - opcua::Type::XmlElement
var.setScalarCopy<std::string>("test");
// finally compiles
var.setScalarCopy<std::string, opcua::Type::String>("test");
You can add template specializations to add conversions for arbitrary types:
namespace opcua {
template <>
struct TypeConverter<std::string> {
using ValueType = std::string;
using NativeType = UA_String;
using ValidTypes = TypeIndexList<UA_TYPES_STRING, UA_TYPES_BYTESTRING, UA_TYPES_XMLELEMENT>;
static void fromNative(const NativeType& src, ValueType& dst) { /* ... */ }
static void toNative(const ValueType& src, NativeType& dst) { /* ... */ }
};
} // namespace opcua
Type Enum opcua::Type |
Type | Typedef | Wrapper | Conversions |
---|---|---|---|---|
Boolean | UA_Boolean |
bool |
||
SByte | UA_SByte |
int8_t |
||
Byte | UA_Byte |
uint8_t |
||
Int16 | UA_Int16 |
int16_t |
||
UInt16 | UA_UInt16 |
uint16_t |
||
Int32 | UA_Int32 |
int32_t |
||
UInt32 | UA_UInt32 |
uint32_t |
||
Int64 | UA_Int64 |
int64_t |
||
UInt64 | UA_UInt64 |
uint64_t |
||
Float | UA_Float |
float |
||
Double | UA_Double |
double |
||
String | UA_String |
opcua::String |
std::string |
|
DateTime | UA_DateTime |
int64_t |
opcua::DateTime |
std::chrono::time_point |
Guid | UA_Guid |
opcua::Guid |
||
ByteString | UA_ByteString |
UA_String |
opcua::ByteString |
std::string |
XmlElement | UA_XmlElement |
UA_String |
opcua::XmlElement |
std::string |
NodeId | UA_NodeId |
opcua::NodeId |
||
ExpandedNodeId | UA_ExpandedNodeId |
opcua::ExpandedNodeId |
||
StatusCode | UA_StatusCode |
uint32_t |
||
QualifiedName | UA_QualifiedName |
opcua::QualifiedName |
||
LocalizedText | UA_LocalizedText |
opcua::LocalizedText |
||
ExtensionObjectDataValue | UA_ExtensionObject |
opcua::ExtensionObjectDataValue |
||
DataValue | UA_DataValue |
opcua::DataValue |
||
Variant | UA_Variant |
opcua::Variant |
||
DiagnosticInfo | UA_DiagnosticInfo |
opcua::DiagnosticInfo |
The library can be built, integrated and installed using CMake.
Please check out the open62541 build options here: https://www.open62541.org/doc/1.3/building.html#build-options
open62541++ provides additional build options:
UAPP_INTERNAL_OPEN62541
: Use internal open62541 library ifON
or search for installed open62541 library ifOFF
UAPP_BUILD_DOCUMENTATION
: Build documentationUAPP_BUILD_EXAMPLES
: Build examples forexamples
directoryUAPP_BUILD_TESTS
: Build an run testsUAPP_ENABLE_CLANG_TIDY
: Enable static code analysis with Clang-TidyUAPP_ENABLE_COVERAGE
: Enable coverage analysisUAPP_ENABLE_SANITIZER_ADDRESS/LEAK/MEMORY/THREAD/UNDEFINED_BEHAVIOUR
: Enable sanitizers
# clone repository
git clone --recursive https://github.com/open62541pp/open62541pp.git
cd open62541pp
# build and run tests
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DUAPP_BUILD_EXAMPLES=ON -DUAPP_BUILD_TESTS=ON ..
cmake --build .
# run tests again (tests run automatically after build step)
ctest --output-on-failure
# coverage report (text or html)
cmake --build . --target open62541pp_coverage_report
cmake --build . --target open62541pp_coverage_report_html
# build documentation
cmake --build . --target open62541pp_doc
# install to system
cmake --install .
Add it to your project as a Git submodule (git submodule add https://github.com/open62541pp/open62541pp.git
) and link it with CMake:
add_subdirectory(extern/open62541pp) # the submodule directory
target_link_library(myexecutable PRIVATE open62541pp::open62541pp)
If you build and install this package to your system, a open62541ppConfig.cmake
file will be generated and installed to your system.
The installed library can be found and linked within CMake:
find_package(open62541pp::open62541pp CONFIG REQUIRED)
target_link_library(myexecutable PRIVATE open62541pp::open62541pp)