Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iox-#1295 Rework icediscovery example #1219

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions iceoryx_examples/icediscovery/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,39 @@ find_package(iceoryx_hoofs CONFIG REQUIRED)
get_target_property(ICEORYX_CXX_STANDARD iceoryx_posh::iceoryx_posh CXX_STANDARD)
include(IceoryxPlatform)

add_executable(iox-offer-service ./iox_offer_service.cpp)
target_link_libraries(iox-offer-service
add_executable(iox-cpp-offer-service ./iox_offer_service.cpp)
target_link_libraries(iox-cpp-offer-service
iceoryx_posh::iceoryx_posh
)
target_compile_options(iox-offer-service PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})
target_compile_options(iox-cpp-offer-service PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})

add_executable(iox-find-service ./iox_find_service.cpp)
target_link_libraries(iox-find-service
add_executable(iox-cpp-find-service ./iox_find_service.cpp)
target_link_libraries(iox-cpp-find-service
iceoryx_posh::iceoryx_posh
)
target_compile_options(iox-find-service PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})
target_compile_options(iox-cpp-find-service PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})

add_executable(iox-wait-for-service ./iox_wait_for_service.cpp ./src/discovery_blocking.cpp)
target_link_libraries(iox-wait-for-service
add_executable(iox-cpp-wait-for-service ./iox_wait_for_service.cpp ./src/discovery_blocking.cpp)
target_link_libraries(iox-cpp-wait-for-service
iceoryx_posh::iceoryx_posh
)

add_executable(iox-discovery-monitor ./iox_discovery_monitor.cpp ./src/discovery_monitor.cpp)
target_link_libraries(iox-discovery-monitor
add_executable(iox-cpp-discovery-monitor ./iox_discovery_monitor.cpp ./src/discovery_monitor.cpp)
target_link_libraries(iox-cpp-discovery-monitor
iceoryx_posh::iceoryx_posh
)

target_include_directories(iox-wait-for-service PRIVATE include)
target_include_directories(iox-discovery-monitor PRIVATE include)
target_include_directories(iox-cpp-wait-for-service PRIVATE include)
target_include_directories(iox-cpp-discovery-monitor PRIVATE include)

target_compile_options(iox-wait-for-service PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})
target_compile_options(iox-discovery-monitor PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})
target_compile_options(iox-offer-service PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})
target_compile_options(iox-cpp-wait-for-service PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})
target_compile_options(iox-cpp-discovery-monitor PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})
target_compile_options(iox-cpp-offer-service PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS})

set_target_properties(iox-offer-service
iox-find-service
iox-wait-for-service
iox-discovery-monitor
set_target_properties(iox-cpp-offer-service
iox-cpp-find-service
iox-cpp-wait-for-service
iox-cpp-discovery-monitor
PROPERTIES
CXX_STANDARD_REQUIRED ON
CXX_STANDARD ${ICEORYX_CXX_STANDARD}
Expand All @@ -67,8 +67,8 @@ set_target_properties(iox-offer-service

# ========================================================== //

install(TARGETS iox-offer-service
iox-find-service
iox-wait-for-service
iox-discovery-monitor
install(TARGETS iox-cpp-offer-service
iox-cpp-find-service
iox-cpp-wait-for-service
iox-cpp-discovery-monitor
RUNTIME DESTINATION bin)
58 changes: 33 additions & 25 deletions iceoryx_examples/icediscovery/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ In addition the applications `iox-wait-for-service` and `iox-discovery-monitor`
to write custom discovery functionality to wait for specific services or monitor
the availability of services respectively.

<!--
## Expected Output

/// @todo #1295 re-record asciicast

[![asciicast](https://asciinema.org/a/476673.svg)](https://asciinema.org/a/476673)
-->

## Code walkthrough

Expand Down Expand Up @@ -47,7 +51,7 @@ It is included via:
On that object we can call the method `findService` which expects the three
mossmaurice marked this conversation as resolved.
Show resolved Hide resolved
service [string identifiers](../../doc/website/getting-started/overview.md#creating-service-descriptions-for-topics)
and a callable which will be applied to all matching services.
In addition we have to specify whether we want to search for publishers (`MessagingPattern::PUB_SUB`)
In addition, we have to specify whether we want to search for publishers (`MessagingPattern::PUB_SUB`)
mossmaurice marked this conversation as resolved.
Show resolved Hide resolved
used in publish-subscribe communication or servers (`MessagingPattern::REQ_RES`) used in
request-response communication.

Expand All @@ -68,7 +72,7 @@ We can search for exactly matching services:
```cpp
serviceDiscovery.findService(iox::capro::IdString_t{"Radar"},
iox::capro::IdString_t{"FrontLeft"},
iox::capro::IdString_t{"Image"},
iox::capro::IdString_t{"Objects"},
printSearchResult,
iox::popo::MessagingPattern::PUB_SUB);
```
Expand All @@ -93,7 +97,8 @@ their services, you should see sometimes 5 `Camera` services and sometimes none.
Start the applications `iox-wait-for-service` and `iox-offer-service`. This can be done in any order,
but for demonstration purposes `iox-offer-service` should be started last.

`iox-wait-for-service` uses a customized service discovery [Discovery](#implementation-of-discovery-with-blocking-wait) which supports to wait for services by including
`iox-wait-for-service` uses a customized service discovery [Discovery](#implementation-of-discovery-with-blocking-wait)
which supports to wait for services by including

<!--[geoffrey][iceoryx_examples/icediscovery/iox_wait_for_service.cpp][include custom discovery]-->
```cpp
Expand Down Expand Up @@ -121,7 +126,7 @@ auto query = [&]() {
```

This is essentially any callable with `bool(void)` signature, but it should depend on the discovery somehow (by capture),
as it is only checked when the service availability changes in some way. Here we require some specific service to be found
as it is only checked when the service availability changes in some way. Here, we require some specific service to be found
before we proceed.

<!--[geoffrey][iceoryx_examples/icediscovery/iox_wait_for_service.cpp][service to wait for]-->
Expand All @@ -137,11 +142,11 @@ Now we can wait until the service discovery changes and the service becomes avai
bool serviceWasAvailable = discovery.waitUntil(query);
```

This wait is blocking until the service was available. If it already is available we do not block and proceed.
This wait blocks until the service is available. If it already is available we do not block and proceed.
It is important that due to the nature of concurrent systems we cannot know that the service is still available
once we return from `waitUntil`, as the application offering the service may have stopped doing so in the meantime.

Usually we will assume that the service is available and may continue, e.g. by creating subscribers and running
Usually, we will assume that the service is available and may continue, e.g. by creating subscribers and running
application specific code.

We can also block until any unspecified change in the service availability occurs
Expand All @@ -154,7 +159,7 @@ This change is relative to the last `findService` call we issued, i.e. if someth
the available services at this point, we wake up and continue.

We then can check any condition we like, but usually it will be most useful to again check discovery-related conditions.
Here we check whether a particular service becomes unavailable (essentially the negation of our query before)
Here, we check whether a particular service becomes unavailable (essentially the negation of our query before)
<!--[geoffrey][iceoryx_examples/icediscovery/iox_wait_for_service.cpp][check service availability]-->
```cpp
if (discovery.findService(service, instance, event).empty())
Expand All @@ -163,7 +168,7 @@ if (discovery.findService(service, instance, event).empty())
}
```

Note that we use a customized `findService` version which returns a result container which can easily be build
Note that we use a customized `findService` version which returns a result container which can easily be built
mossmaurice marked this conversation as resolved.
Show resolved Hide resolved
using the version which takes a function to be applied to all services in the search result.

Once the service becomes unavailable, the application exits.
Expand All @@ -180,13 +185,13 @@ if (discoveryPtr)

### Monitor service availability

If we want to continously monitor the availability of some service or check some discovery condition we can do so by
If we want to continuously monitor the availability of some service or check some discovery condition we can do so by
using e.g. a listener to conditionally execute [callbacks](../callbacks).

To do so, we start the applications `iox-discovery-monitor` and `iox-offer-service`
(again in any order, but for demonstration purposes `iox-offer-service` should be started last).

Again we can use a [Discovery](#implementation-of-discovery-monitoring) customized for this purpose by including
Again, we can use a [Discovery](#implementation-of-discovery-monitoring) customized for this purpose by including

<!--[geoffrey][iceoryx_examples/icediscovery/iox_discovery_monitor.cpp][include custom discovery]-->
```cpp
Expand All @@ -201,7 +206,7 @@ and creating it like so
Discovery discovery;
```

Afterwards we create a callback to be called whenever the service availability changes.
Afterwards, we create a callback to be called whenever the service availability changes.

<!--[geoffrey][iceoryx_examples/icediscovery/iox_discovery_monitor.cpp][create monitoring callback]-->
```cpp
Expand All @@ -222,7 +227,7 @@ auto callback = [&](Discovery& discovery) -> void {
```

This callback essentially checks whether a specific service is available or unavailable and generates output accordingly.
Other reactions are possible as well, such as changing the processing logic of an pplication.
Other reactions are possible as well, such as changing the processing logic of an application.

To start the monitoring, we register the callback

Expand All @@ -240,12 +245,12 @@ When we want to stop monitoring we have to deregister the callback
discovery.deregisterCallback();
```

Here this is done at the very end where it is technically not required, but in a more complex application it could be done
while the application is processing data. The main processing loop of the application is deliberately left empty for simplicty.
Here, this is done at the very end where it is technically not required, but in a more complex application it could be done
while the application is processing data. The main processing loop of the application is deliberately left empty for simplicity.
Usually it would interact with the callback by e.g. changing application behavior whenever the availability of some service changes.

While we only can attach one callback to the general event that the service availability changes in some way, we can generalize the mechanism
here to check for multiple conditions and react to each of them by e.g. calling a specific function.
While we only can attach one callback to the general event that the service availability changes in some way, we can
generalize the mechanism here to check for multiple conditions and react to each of them by e.g. calling a specific function.
These conditions would still need to be checked in the callback we defined though.

### Implementation of Discovery with blocking wait
Expand All @@ -263,7 +268,8 @@ ServiceDiscovery& serviceDiscovery()
}
```

This is useful as the `ServiceDiscovery` may be fairly large and in general there is no point in having multiple `ServiceDiscovery` objects that all have the same purpose and (if updated) same view of the available services.
This is useful as the `ServiceDiscovery` may be fairly large and in general there is no point in having multiple
`ServiceDiscovery` objects that all have the same purpose and (if updated) the same view of the available services.

The key idea is to use a waitset and attach to the event that the service availability changes

Expand Down Expand Up @@ -346,7 +352,8 @@ void Discovery::unblockWait()
}
```

This is can only be called once and makes all future wait calls non-blocking. It is useful to unblock any wait calls to be able to stop the application.
This can only be called once and makes all future wait calls non-blocking. It is useful to unblock any wait calls to be
able to stop the application.

Finally we provide a custom implementation of `findService` which returns a container of our choice, in this case a `std::vector`.

Expand All @@ -363,8 +370,8 @@ ServiceContainer Discovery::findService(const iox::cxx::optional<iox::capro::IdS
}
```

It is implemenented by using the native `findService` call of the `ServiceDiscovery` with an appropriate filter function.
The benefit is that this way we can choose containers which do not necessrily reside on the stack.
It is implemented by using the native `findService` call of the `ServiceDiscovery` with an appropriate filter function.
The benefit is that this way we can choose containers which do not necessarily reside on the stack.

### Implementation of Discovery monitoring

Expand All @@ -391,7 +398,7 @@ m_listener.attachEvent(*m_discovery, iox::runtime::ServiceDiscoveryEvent::SERVIC
```

The callback is stored as a `cxx::function` which does not require dynamic memory (but limits the size of the stored function,
which is relvant e.g. for capturing lambdas). If dynamic memory is no concern we can also use a `std::function`.
which is relevant e.g. for capturing lambdas). If dynamic memory is no concern we can also use a `std::function`.
The callback can be any callable with a `(void)(discovery::Discovery&)` signature.
Again the callback signature can be generalized somewhat but there are constraints to use it with the listener.
Since the listener can only call static or free functions, we use an additional indirection to call the actual callback
Expand All @@ -406,10 +413,10 @@ void Discovery::invokeCallback(ServiceDiscovery*, Discovery* self)
```

As soon as the callback is registered, the listener thread will invoke it on any service availability change.
There is a small caveat though that while callback is called on any change, we can only access
There is a small caveat though that while a callback is called on any change, we can only access
the latest discovery information by e.g. calling `findService`.
This means all intermediate changes cannot be detected, in particular we may encounter an ABA problem of service availabilty:
the service is availalable, becomes unavailable and available again in quick succession.
This means all intermediate changes cannot be detected, in particular we may encounter an ABA problem of service availability:
the service is available, becomes unavailable and available again in quick succession.
If the callback issues a `findService`, it will not observe any change in this case.
As one is usually mainly interested in the available services this can be considered a minor limitation.

Expand All @@ -429,7 +436,8 @@ void Discovery::deregisterCallback()

which detaches the callback from the listener.

As before we built on an `iox::runtime::ServiceDiscovery` by composition and define a custom`findService` function which returns a `std::vector`.
As before we built on an `iox::runtime::ServiceDiscovery` by composition and defined a custom`findService` function
which returns a `std::vector`.

<center>
[Check out icediscovery on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icediscovery){ .md-button } <!--NOLINT github url required for website-->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class Discovery
void unblockWait();

/// @brief get all services matching a findService query
/// @return ServiceContainer, containing the found services
/// @note invokes findService of the native iceoryx ServiceDiscovery API
ServiceContainer findService(const iox::cxx::optional<iox::capro::IdString_t>& service,
const iox::cxx::optional<iox::capro::IdString_t>& instance,
Expand Down
4 changes: 2 additions & 2 deletions iceoryx_examples/icediscovery/iox_find_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ int main()
{
std::cout << "\n=========================================" << std::endl;

std::cout << "\nSearched for {'Radar', 'FrontLeft', 'Image'}. Found the following services:" << std::endl;
std::cout << "\nSearched for {'Radar', 'FrontLeft', 'Objects'}. Found the following services:" << std::endl;
//! [search for unique service]
serviceDiscovery.findService(iox::capro::IdString_t{"Radar"},
iox::capro::IdString_t{"FrontLeft"},
iox::capro::IdString_t{"Image"},
iox::capro::IdString_t{"Objects"},
mossmaurice marked this conversation as resolved.
Show resolved Hide resolved
mossmaurice marked this conversation as resolved.
Show resolved Hide resolved
printSearchResult,
iox::popo::MessagingPattern::PUB_SUB);
//! [search for unique service]
Expand Down
4 changes: 2 additions & 2 deletions iceoryx_examples/icediscovery/iox_offer_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ int main()
iox::runtime::PoshRuntime::initRuntime(APP_NAME);

// offer services by creating publishers
iox::popo::Publisher<uint32_t> radarLeft({"Radar", "FrontLeft", "Image"});
iox::popo::Publisher<uint32_t> radarRight({"Radar", "FrontRight", "Image"});
iox::popo::Publisher<uint32_t> radarLeft({"Radar", "FrontLeft", "Objects"});
iox::popo::Publisher<uint32_t> radarRight({"Radar", "FrontRight", "Objects"});
iox::popo::Publisher<uint32_t> lidarLeft({"Lidar", "FrontLeft", "Counter"});

iox::cxx::vector<iox::popo::Publisher<uint32_t>, 5> cameraPublishers;
Expand Down
8 changes: 6 additions & 2 deletions iceoryx_examples/icediscovery_in_c/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ service discovery. It provides two applications - one offering different
services and one searching for those with different search queries. The
behavior and structure is quite similar to the [icediscovery C++ example](../icediscovery).

<!--
## Expected Output

/// @todo #1295 re-record asciicast

[![asciicast](https://asciinema.org/a/476732.svg)](https://asciinema.org/a/476732)
-->

## Code walkthrough

Expand Down Expand Up @@ -42,10 +46,10 @@ We can now call three different find service functions. Let's start with
<!--[geoffrey][iceoryx_examples/icediscovery_in_c/iox_c_find_service.c][find service and apply callable]-->
```c
iox_service_discovery_find_service_apply_callable(
serviceDiscovery, "Radar", "FrontLeft", "Image", printSearchResult, MessagingPattern_PUB_SUB);
serviceDiscovery, "Radar", "FrontLeft", "Objects", printSearchResult, MessagingPattern_PUB_SUB);
mossmaurice marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we had this discussion but please note that here actually are radar images in the scientific literature. They are just tensors of F^nxmxk. With F some field, mainly real or complex numbers.

E.g. https://en.wikipedia.org/wiki/Synthetic-aperture_radar.

It was perfectly correct, and this change has no value. For the example it does not matter so, so we keep it now.

Copy link
Contributor Author

@mossmaurice mossmaurice Apr 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthiasKillat I know what you mean. IMHO what we demonstrate here is an example right before the perception module rather in the middle of the feedback loop and not in the front where the low-level computation on the raw radar data happens. Typically radar sensors will only provide object lists over the network to be consumed by a central ADAS.

```

which searches for all `{Radar, FrontLeft, Image}` services offered by
which searches for all `{Radar, FrontLeft, Objects}` services offered by
publishers and applies the provided function `printSearchResult` on each of
them. The function must have the signature
`void(const iox_service_description_t)`. Here we pass a function that prints the
Expand Down
4 changes: 2 additions & 2 deletions iceoryx_examples/icediscovery_in_c/iox_c_find_service.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ int main()

printf("\n=========================================\n");

printf("\nSearched for {'Radar', 'FrontLeft', 'Image'}. Found the following services:\n");
printf("\nSearched for {'Radar', 'FrontLeft', 'Objects'}. Found the following services:\n");
//! [find service and apply callable]
iox_service_discovery_find_service_apply_callable(
serviceDiscovery, "Radar", "FrontLeft", "Image", printSearchResult, MessagingPattern_PUB_SUB);
serviceDiscovery, "Radar", "FrontLeft", "Objects", printSearchResult, MessagingPattern_PUB_SUB);
//! [find service and apply callable]

printf("\nSearched for {'Radar', *, *}. Found the following services:\n");
Expand Down
Loading