open62541pp 0.21.2
C++ wrapper of open62541
Loading...
Searching...
No Matches
client_service.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4#include <functional> // invoke
5#include <memory>
6#include <type_traits>
7#include <utility> // forward
8
14#include "open62541pp/typeregistry.hpp" // getDataType
15
16namespace opcua::services::detail {
17
18/**
19 * Adapter to initiate open62541 async client operations with completion tokens.
20 */
21template <typename Response>
22struct AsyncServiceAdapter {
23 using ExceptionCatcher = opcua::detail::ExceptionCatcher;
24
25 template <typename Context>
26 struct CallbackAndContext {
28 std::unique_ptr<Context> context;
29 };
30
31 template <typename CompletionHandler>
32 static auto makeCallbackAndContext(
33 ExceptionCatcher& exceptionCatcher, CompletionHandler&& handler
34 ) {
35 static_assert(std::is_invocable_v<CompletionHandler, Response&>);
36
37 struct Context {
38 ExceptionCatcher* catcher;
39 std::decay_t<CompletionHandler> handler;
40 };
41
42 const auto callback =
43 [](UA_Client*, void* userdata, uint32_t /* reqId */, void* responsePtr) {
44 std::unique_ptr<Context> context{static_cast<Context*>(userdata)};
45 assert(context != nullptr);
46 assert(context->catcher != nullptr);
47 context->catcher->invoke([context = context.get(), responsePtr] {
48 if (responsePtr == nullptr) {
49 throw BadStatus{UA_STATUSCODE_BADUNEXPECTEDERROR};
50 }
51 std::invoke(context->handler, *static_cast<Response*>(responsePtr));
52 });
53 };
54
55 return CallbackAndContext<Context>{
56 callback,
57 std::make_unique<Context>(
58 Context{&exceptionCatcher, std::forward<CompletionHandler>(handler)}
59 )
60 };
61 }
62
63 /**
64 * Initiate open62541 async client operation with user-defined completion token.
65 * @param client Instance of type Client
66 * @param initiation Callable to initiate the async operation. Following signature is expected:
67 * ```
68 * void(
69 * UA_ClientAsyncServiceCallback callback,
70 * void* userdata,
71 * Args... args)
72 * ```
73 * @param token Completion token
74 */
75 template <typename Initiation, typename CompletionToken, typename... Args>
76 static auto initiate(
77 Client& client, Initiation&& initiation, CompletionToken&& token, Args&&... args
78 ) {
79 static_assert(
80 std::is_invocable_v<Initiation, UA_ClientAsyncServiceCallback, void*, Args&&...>
81 );
82
83 return asyncInitiate<Response>(
84 [&client](auto&& handler, auto&& innerInitiation, auto&&... innerArgs) {
85 auto& catcher = opcua::detail::getExceptionCatcher(client);
86 try {
87 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks), false positive?
88 auto callbackAndContext = makeCallbackAndContext(
89 catcher, std::forward<decltype(handler)>(handler)
90 );
91 std::invoke(
92 std::forward<decltype(innerInitiation)>(innerInitiation),
93 callbackAndContext.callback,
94 callbackAndContext.context.get(),
95 std::forward<decltype(innerArgs)>(innerArgs)...
96 );
97 // initiation call might raise an exception
98 // transfer ownership to the callback afterwards
99 callbackAndContext.context.release();
100 } catch (...) {
101 catcher.setException(std::current_exception());
102 }
103 },
104 std::forward<CompletionToken>(token),
105 std::forward<Initiation>(initiation),
106 std::forward<Args>(args)...
107 );
108 }
109};
110
111/// Async client service requests.
112template <typename Request, typename Response, typename CompletionToken>
113auto sendRequestAsync(Client& client, const Request& request, CompletionToken&& token) {
114 return AsyncServiceAdapter<Response>::initiate(
115 client,
116 [&client](
117 UA_ClientAsyncServiceCallback callback, void* userdata, const Request& innerRequest
118 ) {
120 opcua::detail::getHandle(client),
121 &innerRequest,
122 &getDataType<Request>(),
123 callback,
124 &getDataType<Response>(),
125 userdata,
126 nullptr
127 ));
128 },
129 std::forward<CompletionToken>(token),
130 request
131 );
132}
133
134/// Sync client service requests.
135template <typename Request, typename Response>
136Response sendRequest(Client& client, const Request& request) noexcept {
137 Response response{};
139 opcua::detail::getHandle(client),
140 &request,
141 &getDataType<Request>(),
142 &response,
143 &getDataType<Response>()
144 );
145 return response;
146}
147
148} // namespace opcua::services::detail
void(* UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response)
UA_StatusCode UA_THREADSAFE __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)
void UA_THREADSAFE __UA_Client_Service(UA_Client *client, const void *request, const UA_DataType *requestType, void *response, const UA_DataType *responseType)
struct UA_Client UA_Client
constexpr void throwIfBad(UA_StatusCode code)
Check the status code and throw a BadStatus exception if the status code is bad.
Definition exception.hpp:85