Details of the SDK

Thread model.

The SDK is designed to run in multithreaded mode. It needs few categories of threads. Number of threads for each category can be set in the SDK initialization function call void OWA::OpcUa::Utils::initSdk(int numberOfIoThreads = 1, int numberOfCallbackThreads = 1);.

I/O threads.

At least one thread is used for TCP communications.

Callback threads

The SDK performs callbacks when different kinds of events occur: when state of connection is changed, OPC UA request is complete, or when publish response is received. These callbacks are made from threads different than I/O threads, and belong to separate thread pool. By default, one thread is created for such callbacks.

General thread pool.

The SDK provides general thread pool which can be used by the application for its own purposes. This instance of the thread pool can be retrieved by calling function std::shared_ptr<ThreadPool> Utils::getThreadPool();. To clean this thread pool, function void Utils::closeThreadPool(); is used. To add new tasks to the pool, method enqueue is used.

Timer

To run tasks (functions) at certiain point of time in the future, the SDK uses Timer class. This class creates own thread maintain list of tasks. Tasks launched in threads belonging to the Callbacks Thread Pool. Its instance can be retrieved by calling of the function std::shared_ptr<Timer> Utils::getTimer();. Tasks can be added by using of its add method, which returns identifier of the task. To remove tasks, call method remove, which accepts timer identifier as argument. Note that if the task is already running, it cannot be canceled.

Particularly, Timer is used by the SDK to schedule periodic calls of Read requests to check OPC UA Servers state, or to reconnect when communication with the server is closed un-expectedly.

Initialization and clean-up.

The SDK can be used without initialization. In this case default thread model settings will be used. To initialize the SDK with different settings, function Utils::initSDK can be used.

Clean-up of the SDK can also be omitted. In this case, some resourses can be reported as not un-initialized by tools such as memory leak detectors. To free all resources used by the SDK in controlled way, function Utils::cleanSDK can be used.

Connection class

The Connection class is major entry point for OPC UA Client functionality of the SDK. Instances of the class are created by calling static create method of it. This method have a few overrides, with different types fo arguments.

In simplest use case, it is enough to define Endpoint URL of the OPC UA Server to which you want to connect.

Fine turning of connection settings.

For more detailed control of connection settings, struct ClientConfiguration can be used. In turn, this struct has members defining different aspects of connection to the server,, such as information about the client application (member applicationDesctiption), about the server (member serverInfo), about application instance certificate (member certificateSettings), and others such as timeouts for different types of requests, settings at transport level, etc.

Smart connection and re-connections

In order to make sure that the SDK connects to the desired server, enough information should be given. It is possible to define this information in different ways. to most commonly used way might be using of the Endpoint URL. But in fact server can change endpoints after their configuration change. For example, port number of even host name can be changed. The SDK has built-in logic to find correct server based on additional settings such as server URI, application name, discovery URL, and using discovery services FindServers and GetEndpoints.

The SDK also uses GetEndpoints request in cases if connection in secured mode is required, to retrieve server certificate.

When connection with the server is lost, the SDK can re-discover the server’s endpoint, using discovery URL and other information about the server, and re-connect to the it, after its restart or endpoint URL change.

Note

If there are created subscriptions and monitored items created on the server, they will be re-created by the SDK automatically after re-connection.

How to connect to the server

In order to initiate connection process asynchronously, call method Connection::connect, on the instance of the Connection class. It takes optional argument - a function, wich will be called when connection process is completed. The method returns shared future object, with argument type OperationResult. It is possible to call its get method, to wait until connection process completion - so in this way the SDK can be used synchronously.

How to disconnect from the server.

To disconnect from the server, call method Connection::disconnect.

Request and response structs

Once connection with OPC UA server is established, user application can start calling OPC UA Services, such as Browse, Read, Write, etc.

To represent OPC UA Services, structs with the same name are used. For example, to call Browse request, instance of the Browse struct is used.

Note

Almost always arguments are passed and returned using smart pointers, particularly std::shared_ptr. This guarantees that instances of objects exist while they are in use and deleted after going out of scope.

Note

Request and reponse structs practically exactly mimic OPC UA Services as described in OPC UA Specification Part 4: they have the same name for members, as defined in the spec.

How to call OPC UA Services

  • Create instance of the struct of desired type.
  • Call Connection::send method. Optionally callback function can be passed - it will be called when the response for the request is received from the server. When this callback function is called, both original request and received response are passed as arguments. So, for the user application there is no need to keep original request. The send method returns std::future object with type of the response. This future object can be stored somewhere if required to get the result of completion later.
  • Call result can be obtained in 2 ways: - via returned future object, by calling method get on it; - via passed callback function.

Synchronous and asynchronous communication.

As already mentioned above, to support both asynchronous and synchronous call modes, methods return std::shared_future or std::future, assiciated with type of value desired to be returned. to get call result immediately, in synchronous mode, you can call get method of the future object.

To handle call result asynchronously, pass callback function - it will be called, when operation is completed.

Note

It is possible to access call result in both ways: via callback funciton and future object. First, cannback function is called, and after that result value on the futue object is set.