File: README.md

package info (click to toggle)
iceoryx 2.0.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 11,260 kB
  • sloc: cpp: 94,127; ansic: 1,443; sh: 1,436; python: 1,377; xml: 80; makefile: 61
file content (192 lines) | stat: -rw-r--r-- 6,681 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# icehello

## Introduction

This example demonstrates a basic data transmission with zero-copy inter-process communication (IPC).
It provides a publisher and a subscriber application.

## Expected Output

[![asciicast](https://asciinema.org/a/407357.svg)](https://asciinema.org/a/407357)

## Code walkthrough

### Publisher

At first, we need to define what kind of data type the publisher and subscriber application will exchange:

<!--[geoffrey][iceoryx_examples/icehello/topic_data.hpp][radar object]-->
```cpp
struct RadarObject
{
    double x = 0.0;
    double y = 0.0;
    double z = 0.0;
};
```

It is included via:

<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][include topic]-->
```cpp
#include "topic_data.hpp"
```

Next, we include the publisher and the runtime:

<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][include]-->
```cpp
#include "iceoryx_posh/popo/publisher.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"
```

We create a runtime object to communicate with the RouDi daemon. We use a unique string for identifying our application:

<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][initialize runtime]-->
```cpp
constexpr char APP_NAME[] = "iox-cpp-publisher-helloworld";
iox::runtime::PoshRuntime::initRuntime(APP_NAME);
```

Now we create a publisher instance for our charming struct. Notice that the topic type is passed as a template
parameter:

<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][create publisher]-->
```cpp
iox::popo::Publisher<RadarObject> publisher({"Radar", "FrontLeft", "Object"});
```

The three strings which are passed as parameter to the constructor of `iox::popo::Publisher` define our
`capro::ServiceDescription`. `capro` stands for **ca**nionical **pro**tocol and is used to abstract different
[SoA](https://en.wikipedia.org/wiki/Service-oriented_architecture) protocols. `Radar` is the service name, `FrontLeft`
an instance of the service `Radar` and the third string the specific event `Object` of the instance.
In iceoryx, a publisher and a subscriber are connected only if all three IDs match.

For exiting on Ctrl+C, we use the `SignalWatcher`
<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][include sig watcher]-->
```cpp
#include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp"
```

and loop in our `while` loop until it states that `SIGINT` or `SIGTERM` was sent via
the function `hasTerminationRequested`.
<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][wait for term]-->
```cpp
while (!iox::posix::hasTerminationRequested())
```

In order to send our sample, we loan some shared memory inside the `while` loop:

<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][loan]-->
```cpp
auto loanResult = publisher.loan();
```

If loaning was successful, we assign the incremented counter to all three values in `RadarObject` and `publish()` to the subscriber application:

<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][publish]-->
```cpp
if (!loanResult.has_error())
{
    auto& sample = loanResult.value();
    // Sample can be held until ready to publish
    sample->x = ct;
    sample->y = ct;
    sample->z = ct;
    sample.publish();
}
```

In case an error occurred during loaning, we need to handle it:

<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][error]-->
```cpp
else
{
    auto error = loanResult.get_error();
    // Do something with error
    std::cerr << "Unable to loan sample, error code: " << error << std::endl;
}
```

Topics are printed and published every second:

<!--[geoffrey][iceoryx_examples/icehello/iox_publisher_helloworld.cpp][msg]-->
```cpp
std::cout << APP_NAME << " sent value: " << ct << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
```

### Subscriber

The subscriber needs to have similar includes, but unlike the publisher `subscriber.hpp` is included:

<!--[geoffrey][iceoryx_examples/icehello/iox_subscriber_helloworld.cpp][include]-->
```cpp
#include "topic_data.hpp"

#include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp"
#include "iceoryx_posh/popo/subscriber.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"
```

As well as the publisher, the subscriber needs to register with the daemon RouDi:

<!--[geoffrey][iceoryx_examples/icehello/iox_subscriber_helloworld.cpp][initialize runtime]-->
```cpp
constexpr char APP_NAME[] = "iox-cpp-subscriber-helloworld";
iox::runtime::PoshRuntime::initRuntime(APP_NAME);
```

Next, the subscriber object is created, again passing the topic type `RadarObject` as template parameter:

<!--[geoffrey][iceoryx_examples/icehello/iox_subscriber_helloworld.cpp][initialize subscriber]-->
```cpp
iox::popo::Subscriber<RadarObject> subscriber({"Radar", "FrontLeft", "Object"});
```

Publisher and subscriber will only be connected if they both use exactly these same three strings, our `capro::ServiceDescription`.

Inside the `while` loop, we take the sample from shared memory and print it if we acquired it successfully.

<!--[geoffrey][iceoryx_examples/icehello/iox_subscriber_helloworld.cpp][receive]-->
```cpp
auto takeResult = subscriber.take();
if (!takeResult.has_error())
{
    std::cout << APP_NAME << " got value: " << takeResult.value()->x << std::endl;
}
```

In case an error occurred during taking, we need to handle it:

<!--[geoffrey][iceoryx_examples/icehello/iox_subscriber_helloworld.cpp][error]-->
```cpp
if (takeResult.get_error() == iox::popo::ChunkReceiveResult::NO_CHUNK_AVAILABLE)
{
    std::cout << "No chunk available." << std::endl;
}
else
{
    std::cout << "Error receiving chunk." << std::endl;
}
```

The subscriber application polls for the sample ten times faster than the publisher is sending it.
Therefore no samples should be missed, but not every time the subscriber tries
to take a sample, it will get some. In this case, we print "No chunk available.".

<!--[geoffrey][iceoryx_examples/icehello/iox_subscriber_helloworld.cpp][wait]-->
```cpp
std::this_thread::sleep_for(std::chrono::milliseconds(100));
```

Increasing the polling rate is just one approach for reliable communication.
[iceoptions](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/iceoptions) explains how to
configure the history size of a subscriber. In the
[WaitSet](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/waitset) example you learn how to
avoid polling altogether.

<center>
[Check out icehello on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/icehello){ .md-button }
</center>