open62541pp 0.15.0
C++ wrapper of open62541
Loading...
Searching...
No Matches
client_services.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4#include <functional> // invoke
5#include <memory>
6#include <tuple>
7#include <type_traits>
8#include <utility> // forward
9
10#include "open62541pp/async.hpp"
17#include "open62541pp/typeregistry.hpp" // getDataType
18
20
21/**
22 * Adapter to initiate open62541 async client operations with completion tokens.
23 */
24template <typename Response>
27
28 template <typename Context>
33
34 template <typename CompletionHandler, typename TransformResponse>
36 ExceptionCatcher& exceptionCatcher,
37 TransformResponse&& transformResponse,
38 CompletionHandler&& completionHandler
39 ) {
40 static_assert(std::is_invocable_v<TransformResponse, Response&>);
41 using TransformResult = std::invoke_result_t<TransformResponse, Response&>;
42 static_assert(std::is_invocable_v<CompletionHandler, TransformResult&>);
43 using Context = std::tuple<ExceptionCatcher&, TransformResponse, CompletionHandler>;
44
45 auto callback = [](UA_Client*, void* userdata, uint32_t /* reqId */, void* responsePtr) {
46 assert(userdata != nullptr);
47 std::unique_ptr<Context> context{static_cast<Context*>(userdata)};
48 auto& catcher = std::get<ExceptionCatcher&>(*context);
49 catcher.invoke([&] {
50 if (responsePtr == nullptr) {
52 }
53 Response& response = *static_cast<Response*>(responsePtr);
54 auto result = std::invoke(std::get<TransformResponse>(*context), response);
55 std::invoke(std::get<CompletionHandler>(*context), result);
56 });
57 };
58
60 callback,
61 std::make_unique<Context>(
62 exceptionCatcher,
63 std::forward<TransformResponse>(transformResponse),
64 std::forward<CompletionHandler>(completionHandler)
65 )
66 };
67 }
68
69 /**
70 * Initiate open62541 async client operation with user-defined completion token.
71 * @param client Instance of type Client
72 * @param initiation Callable to initiate the async operation. Following signature is expected:
73 * `void(UA_ClientAsyncServiceCallback callback, void* userdata)`
74 * @param transformResponse Callable to transform the `Response` type to the desired output
75 * @param token Completion token
76 */
77 template <typename Initiation, typename TransformResponse, typename CompletionToken>
78 static auto initiate(
79 Client& client,
80 Initiation&& initiation,
81 TransformResponse&& transformResponse,
82 CompletionToken&& token
83 ) {
84 static_assert(std::is_invocable_v<Initiation, UA_ClientAsyncServiceCallback, void*>);
85 using TransformResult = std::invoke_result_t<TransformResponse, Response&>;
86
88 [&](auto&& completionHandler, auto&& transform) {
89 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks), false positive?
90 auto callbackAndContext = createCallbackAndContext(
91 opcua::detail::getContext(client).exceptionCatcher,
92 std::forward<decltype(transform)>(transform),
93 std::forward<decltype(completionHandler)>(completionHandler)
94 );
95
96 std::invoke(
97 std::forward<Initiation>(initiation),
98 callbackAndContext.callback,
99 callbackAndContext.context.release() // transfer ownership to callback
100 );
101 },
102 std::forward<CompletionToken>(token),
103 std::forward<TransformResponse>(transformResponse)
104 );
105 }
106};
107
108template <typename Request, typename Response, typename TransformResponse, typename CompletionToken>
110 Client& client,
111 const Request& request,
112 TransformResponse&& transformResponse,
113 CompletionToken&& token
114) {
116 client,
117 [&](UA_ClientAsyncServiceCallback callback, void* userdata) {
118 const auto status = __UA_Client_AsyncService(
119 client.handle(),
120 &request,
122 callback,
124 userdata,
125 nullptr
126 );
128 Response response{};
129 response.responseHeader.serviceResult = status;
130 callback(client.handle(), userdata, {}, &response);
131 }
132 },
133 std::forward<TransformResponse>(transformResponse),
134 std::forward<CompletionToken>(token)
135 );
136}
137
138/// Completion token for sync client operations.
140
141/// Overload for sync client requests.
142template <typename Request, typename Response, typename TransformResponse>
143static auto sendRequest(
144 Client& client,
145 const Request& request,
146 TransformResponse&& transformResponse,
147 SyncOperation /*unused*/
148) noexcept(std::is_nothrow_invocable_v<TransformResponse, Response&>) {
149 Response response{};
150 const auto responseDeleter = opcua::detail::ScopeExit([&] {
152 });
153
155 client.handle(), &request, &getDataType<Request>(), &response, &getDataType<Response>()
156 );
157
158 return std::invoke(std::forward<TransformResponse>(transformResponse), response);
159}
160
161} // namespace opcua::services::detail
Exception for bad status codes from open62541 UA_STATUSCODE_*.
Definition exception.hpp:15
High-level client class.
Definition client.hpp:63
UA_Client * handle() noexcept
Catch & store exceptions from user-defined callbacks in an exception-unaware context (open62541).
General-purpose scope guard intended to call its exit function when a scope is exited.
Definition scope.hpp:14
void(* UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response)
void __UA_Client_Service(UA_Client *client, const void *request, const UA_DataType *requestType, void *response, const UA_DataType *responseType)
UA_StatusCode __UA_Client_AsyncService(UA_Client *client, const void *request, const UA_DataType *requestType, UA_ClientAsyncServiceCallback callback, const UA_DataType *responseType, void *userdata, UA_UInt32 *requestId)
auto asyncInitiate(Initiation &&initiation, CompletionToken &&token, Args &&... args)
Definition async.hpp:40
ClientContext * getContext(UA_Client *client) noexcept
constexpr void clear(T &native, const UA_DataType &type) noexcept
constexpr bool isBad(UA_StatusCode code) noexcept
Definition exception.hpp:62
auto sendRequest(Client &client, const Request &request, TransformResponse &&transformResponse, CompletionToken &&token)
const UA_DataType & getDataType() noexcept
#define UA_STATUSCODE_BADUNEXPECTEDERROR
Adapter to initiate open62541 async client operations with completion tokens.
static auto createCallbackAndContext(ExceptionCatcher &exceptionCatcher, TransformResponse &&transformResponse, CompletionHandler &&completionHandler)
static auto initiate(Client &client, Initiation &&initiation, TransformResponse &&transformResponse, CompletionToken &&token)
Initiate open62541 async client operation with user-defined completion token.
Completion token for sync client operations.
UA_StatusCode status