Skip to content

Commit 4e24385

Browse files
dkroenkeelBoberido
authored andcommitted
iox-#27 Rewrite listener example client to use waitset
Signed-off-by: Dietrich Krönke <[email protected]>
1 parent 2a6032d commit 4e24385

File tree

6 files changed

+245
-195
lines changed

6 files changed

+245
-195
lines changed

iceoryx_examples/request_response/CMakeLists.txt

+7-7
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,19 @@ target_compile_options(
8080
${ICEORYX_SANITIZER_FLAGS}
8181
)
8282

83-
## C++ typed API client with Listener
83+
## C++ typed API client with WaitSet
8484
add_executable(
85-
iox-cpp-request-response-listener-client
86-
client_cxx_listener.cpp
85+
iox-cpp-request-response-waitset-client
86+
client_cxx_waitset.cpp
8787
)
8888
target_link_libraries(
89-
iox-cpp-request-response-listener-client
89+
iox-cpp-request-response-waitset-client
9090
PRIVATE
9191
iceoryx_hoofs::iceoryx_hoofs
9292
iceoryx_posh::iceoryx_posh
9393
)
9494
target_compile_options(
95-
iox-cpp-request-response-listener-client
95+
iox-cpp-request-response-waitset-client
9696
PRIVATE
9797
${ICEORYX_WARNINGS}
9898
${ICEORYX_SANITIZER_FLAGS}
@@ -140,7 +140,7 @@ set_target_properties(
140140
iox-cpp-request-response-untyped-server
141141
iox-cpp-request-response-basic-client
142142
iox-cpp-request-response-basic-server
143-
iox-cpp-request-response-listener-client
143+
iox-cpp-request-response-waitset-client
144144
iox-cpp-request-response-listener-server
145145
PROPERTIES
146146
CXX_STANDARD_REQUIRED ON
@@ -154,7 +154,7 @@ install(
154154
iox-cpp-request-response-untyped-server
155155
iox-cpp-request-response-basic-client
156156
iox-cpp-request-response-basic-server
157-
iox-cpp-request-response-listener-client
157+
iox-cpp-request-response-waitset-client
158158
iox-cpp-request-response-listener-server
159159
RUNTIME DESTINATION bin
160160
)

iceoryx_examples/request_response/README.md

+108-64
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,70 @@
55
This example demonstrates how to use iceoryx in a client-server architecture
66
using the request-response communication pattern.
77

8-
## Expected output
8+
## Expected output basic server-client example
99

1010
[![asciicast](https://asciinema.org/a/469913.svg)](https://asciinema.org/a/469913)
1111

1212
## Code walkthrough
1313

14-
### Client
14+
In the following scenario the client (client_cxx_waitset.cpp) is using the `Waitset` to wait for a response from the server
15+
(server_cxx_listener.cpp) that uses the `Listener` API for taking and processing the requests.
1516

16-
At first, the includes for the client port and request-response types and runtime are needed.
17-
<!-- [geoffrey] [geoffrey][iceoryx_examples/request_response/client_cxx_basic.cpp] [iceoryx includes] -->
17+
The client is inspired by the `iox-cpp-waitset-basic` example from the `waitset` folder and the server from the
18+
`iox-cpp-callbacks-subscriber` example in the `callbacks` folder.
19+
20+
This is the most recommended way to create an efficient server-client combination with iceoryx.
21+
22+
### Client using Waitset
23+
24+
At first, the includes for the client port, request-response types, WaitSet, and runtime are needed.
25+
<!-- [geoffrey] [geoffrey][iceoryx_examples/request_response/client_cxx_waitset.cpp] [iceoryx includes] -->
1826
```cpp
1927
#include "request_and_response_types.hpp"
2028

2129
#include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp"
2230
#include "iceoryx_posh/popo/client.hpp"
31+
#include "iceoryx_posh/popo/wait_set.hpp"
2332
#include "iceoryx_posh/runtime/posh_runtime.hpp"
2433
```
2534

2635
Next, the iceoryx runtime is initialized. With this call,
2736
the application will be registered at `RouDi`, the routing and discovery daemon.
28-
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_basic.cpp] [initialize runtime] -->
37+
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_waitset.cpp] [initialize runtime] -->
2938
```cpp
30-
constexpr char APP_NAME[] = "iox-cpp-request-response-client-basic";
31-
iox::runtime::PoshRuntime::initRuntime(APP_NAME);
39+
iox::runtime::PoshRuntime::initRuntime("iox-cpp-waitset-basic");
3240
```
3341
34-
After creating the runtime, the client port is created based on a ServiceDescription.
35-
<!--[geoffrey][iceoryx_examples/request_response/client_cxx_basic.cpp][create client]-->
42+
After creating the runtime, the client port is created and attached to the Waitset.
43+
44+
<!--[geoffrey][iceoryx_examples/request_response/client_cxx_waitset.cpp][create client]-->
3645
```cpp
3746
iox::popo::Client<AddRequest, AddResponse> client({"Example", "Request-Response", "Add"});
3847
```
3948

49+
<!--[geoffrey][iceoryx_examples/request_response/client_cxx_waitset.cpp][create waitset]-->
50+
```cpp
51+
waitset.emplace();
52+
waitset->attachState(client, iox::popo::ClientState::HAS_RESPONSE).or_else([](auto) {
53+
std::cerr << "failed to attach client" << std::endl;
54+
std::exit(EXIT_FAILURE);
55+
});
56+
```
57+
4058
The main goal of the client is to request from the server the sum of two numbers that the
4159
client sends. When to sum is received from the server, the received sum is re-used to insert
4260
it to the `addend` of the next request to send.
4361
This calculates a Fibonacci sequence.
44-
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_basic.cpp] [[send requests in a loop]] -->
62+
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_waitset.cpp] [[mainloop]] -->
4563
```cpp
46-
uint64_t fibonacciLast = 0;
47-
uint64_t fibonacciCurrent = 1;
48-
int64_t requestSequenceId = 0;
49-
int64_t expectedResponseSequenceId = requestSequenceId;
5064
while (!iox::posix::hasTerminationRequested())
5165
{
5266
// send request to server for sum up two numbers
5367
54-
// the server polls with an interval of 100ms
55-
constexpr std::chrono::milliseconds DELAY_TIME{150U};
56-
std::this_thread::sleep_for(DELAY_TIME);
68+
// We block and wait for samples to arrive, when the ime is up we send the request again
69+
auto notificationVector = waitset.timedWait(iox::units::Duration::fromSeconds(5));
5770
58-
// receive sum
71+
// when response received, take the sample and process it
5972
6073
constexpr std::chrono::milliseconds SLEEP_TIME{950U};
6174
std::this_thread::sleep_for(SLEEP_TIME);
@@ -68,16 +81,17 @@ Additionally, the sample is marked with a sequence id that is incremented before
6881
every send cycle to ensure a correct ordering of the messages
6982
(`request.getRequestHeader().setSequenceId()`).
7083
The request is transmitted to the server via the `send()` API.
71-
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_basic.cpp] [[send request]] -->
84+
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_waitset.cpp] [[send request]] -->
7285
```cpp
7386
client.loan()
7487
.and_then([&](auto& request) {
75-
request.getRequestHeader().setSequenceId(requestSequenceId);
76-
expectedResponseSequenceId = requestSequenceId;
77-
requestSequenceId += 1;
78-
request->augend = fibonacciLast;
79-
request->addend = fibonacciCurrent;
80-
std::cout << APP_NAME << " Send Request: " << fibonacciLast << " + " << fibonacciCurrent << std::endl;
88+
request.getRequestHeader().setSequenceId(ctx.requestSequenceId);
89+
ctx.expectedResponseSequenceId = ctx.requestSequenceId;
90+
ctx.requestSequenceId += 1;
91+
request->augend = ctx.fibonacciLast;
92+
request->addend = ctx.fibonacciCurrent;
93+
std::cout << APP_NAME << " Send Request: " << ctx.fibonacciLast << " + " << ctx.fibonacciCurrent
94+
<< std::endl;
8195
request.send();
8296
})
8397
.or_else([](auto& error) {
@@ -90,79 +104,109 @@ and extract the sequence id with `response.getResponseHeader().getSequenceId()`.
90104
When the server response comes in the correct order, the received sum is re-used to
91105
insert it to the `addend` of the next request to send.
92106
93-
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_basic.cpp] [[take response]] -->
107+
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_waitset.cpp] [[take response]] -->
94108
```cpp
95109
while (client.take().and_then([&](const auto& response) {
96110
auto receivedSequenceId = response.getResponseHeader().getSequenceId();
97-
if (receivedSequenceId == expectedResponseSequenceId)
111+
if (receivedSequenceId == ctx.expectedResponseSequenceId)
98112
{
99-
fibonacciLast = fibonacciCurrent;
100-
fibonacciCurrent = response->sum;
101-
std::cout << APP_NAME << " Got Response : " << fibonacciCurrent << std::endl;
113+
ctx.fibonacciLast = ctx.fibonacciCurrent;
114+
ctx.fibonacciCurrent = response->sum;
115+
std::cout << APP_NAME << " Got Response : " << ctx.fibonacciCurrent << std::endl;
102116
}
103117
else
104118
{
105-
std::cout << "Got Response with outdated sequence ID! Expected = " << expectedResponseSequenceId
106-
<< "; Actual = " << receivedSequenceId << "! -> skip" << std::endl;
119+
std::cout << "Got Response with outdated sequence ID! Expected = "
120+
<< ctx.expectedResponseSequenceId << "; Actual = " << receivedSequenceId
121+
<< "! -> skip" << std::endl;
107122
}
108123
}))
109124
{
110-
};
125+
}
111126
```
112127

113-
### Server
128+
### Server using Listener
114129

115130
At first, the includes for the server port and request-response types and runtime are needed.
116-
<!-- [geoffrey] [geoffrey][iceoryx_examples/request_response/server_cxx_basic.cpp] [iceoryx includes] -->
131+
<!-- [geoffrey] [geoffrey][iceoryx_examples/request_response/server_cxx_listener.cpp] [iceoryx includes] -->
117132
```cpp
118133
#include "request_and_response_types.hpp"
119134

120135
#include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp"
136+
#include "iceoryx_posh/popo/listener.hpp"
137+
#include "iceoryx_posh/popo/notification_callback.hpp"
121138
#include "iceoryx_posh/popo/server.hpp"
122139
#include "iceoryx_posh/runtime/posh_runtime.hpp"
123140
```
124141

125-
Next, the iceoryx runtime is initialized. With this call,
126-
the application will be registered at `RouDi`, the routing and discovery daemon.
127-
<!-- [geoffrey] [iceoryx_examples/request_response/server_cxx_basic.cpp] [initialize runtime] -->
142+
First, a callback is created that shall be called when the server receives a response.
143+
In this case the calculation and the sending of the response is done in the listener callback.
144+
If there are more resource-consuming tasks,
145+
this could also be outsourced with a thread pool to handle the requests
146+
147+
<!--[geoffrey][iceoryx_examples/request_response/server_cxx_listener.cpp][request callback]-->
148+
```cpp
149+
void onRequestReceived(iox::popo::Server<AddRequest, AddResponse>* server)
150+
{
151+
while (server->take().and_then([&](const auto& request) {
152+
std::cout << APP_NAME << " Got Request: " << request->augend << " + " << request->addend << std::endl;
153+
154+
server->loan(request)
155+
.and_then([&](auto& response) {
156+
response->sum = request->augend + request->addend;
157+
std::cout << APP_NAME << " Send Response: " << response->sum << std::endl;
158+
response.send();
159+
})
160+
.or_else([](auto& error) {
161+
std::cout << "Could not allocate Response! Return value = " << static_cast<uint64_t>(error)
162+
<< std::endl;
163+
});
164+
}))
165+
{
166+
}
167+
}
168+
```
169+
170+
Maybe: The server provides the `take()` method for receiving requests and the `loan()` and `send()` methods
171+
for sending the responses with the sum of the two numbers.
172+
173+
Next, the iceoryx runtime is initialized.
174+
<!-- [geoffrey] [iceoryx_examples/request_response/server_cxx_listener.cpp] [initialize runtime] -->
128175
```cpp
129176
constexpr char APP_NAME[] = "iox-cpp-request-response-server-basic;
130177
iox::runtime::PoshRuntime::initRuntime(APP_NAME);
131178
```
132179

133180
After creating the runtime, the server port is created based on a ServiceDescription.
134-
<!--[geoffrey][iceoryx_examples/request_response/server_cxx_basic.cpp][create server]-->
181+
<!--[geoffrey][iceoryx_examples/request_response/server_cxx_listener.cpp][create server]-->
135182
```cpp
136183
iox::popo::Server<AddRequest, AddResponse> server({"Example", "Request-Response", "Add"});
137184
```
138185
139-
In the main loop, the server receives the requests from the client with
140-
`take()` and prepares with `loan()` a sample for the response.
141-
The received two numbers `augend` and `addend` are added and transmitted
142-
with the `send()` API.
143-
<!-- [geoffrey] [iceoryx_examples/request_response/server_cxx_basic.cpp] [[process requests in a loop] [take request] [send response]] -->
186+
Now we want to listen to an incoming server event and want to fire the previously created callback.
187+
This is done with the following call:
188+
<!-- [geoffrey] [iceoryx_examples/request_response/server_cxx_listener.cpp][attach listener] -->
144189
```cpp
145-
while (!iox::posix::hasTerminationRequested())
146-
{
147-
server.take().and_then([&](const auto& request) {
148-
std::cout << APP_NAME << " Got Request: " << request->augend << " + " << request->addend << std::endl;
149-
150-
server.loan(request)
151-
.and_then([&](auto& response) {
152-
response->sum = request->augend + request->addend;
153-
std::cout << APP_NAME << " Send Response: " << response->sum << std::endl;
154-
response.send();
155-
})
156-
.or_else([&](auto& error) {
157-
std::cout << APP_NAME
158-
<< "Could not allocate Response! Return value = " << static_cast<uint64_t>(error)
159-
<< std::endl;
160-
});
161-
});
190+
listener
191+
.attachEvent(
192+
server, iox::popo::ServerEvent::REQUEST_RECEIVED, iox::popo::createNotificationCallback(onRequestReceived))
193+
.or_else([](auto) {
194+
std::cerr << "unable to attach server" << std::endl;
195+
std::exit(EXIT_FAILURE);
196+
});
197+
```
162198

163-
constexpr std::chrono::milliseconds SLEEP_TIME{100U};
164-
std::this_thread::sleep_for(SLEEP_TIME);
165-
}
199+
With that the preparation is done and the main thread can just sleep or do other things:
200+
201+
<!-- [geoffrey] [iceoryx_examples/request_response/server_cxx_listener.cpp][wait for termination] -->
202+
```cpp
203+
iox::posix::waitForTerminationRequest();
204+
```
205+
206+
Once the user wants to shutdown the server, the server event is detached from the listener:
207+
<!-- [geoffrey] [iceoryx_examples/request_response/server_cxx_listener.cpp][wait for termination] -->
208+
```cpp
209+
listener.detachEvent(server, iox::popo::ServerEvent::REQUEST_RECEIVED);
166210
```
167211

168212
<center>

0 commit comments

Comments
 (0)