Skip to content

Small cleanups all over the design document. (backport #662) #663

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

Merged
merged 1 commit into from
Jun 13, 2025
Merged
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
84 changes: 42 additions & 42 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The end result is that users can use ROS 2 to send and receive data over Zenoh,

There is more detail on each item below, but a brief overview on how this is accomplished is the following:

* It is assumed that a Zenoh router is running on the local system. This router will be used for discovery and host-to-host communication. However it is *not* used for intra-host comms (i.e., as a message broker); that is done via direct peer-to-peer connections.
* It is assumed that a Zenoh router is running on the local system. This router will be used for discovery and host-to-host communication. However it is *not* used for intra-host communication (i.e., as a message broker); data is sent via direct peer-to-peer connections.
* Each "context" in ROS 2 is mapped to a single Zenoh "session". That means that there may be many publishers, subscriptions, services, and clients sharing the same session.
* Every "context" has a local "graph cache" that keeps track of the details of the network graph of ROS 2 entities.
* Zenoh publishers, subscriptions, services, and clients are created or destroyed when the corresponding RMW APIs are called.
Expand Down Expand Up @@ -88,7 +88,7 @@ Hence `rmw_zenoh_cpp` requires the Zenoh router to be running.

It should be noted that when building upstream Zenoh from source, a `zenohd` binary is created which is the router.
`rmw_zenoh_cpp` has its own simplified version of the router that nonetheless uses most of the same code.
This was done so that Zenoh didn't have to be vendored twice (once for zenoh-c and once for zenohd), and so that the router could be more easily integrated into the ROS 2 package format.
This was done so that Zenoh didn't have to be vendored twice (once for zenoh-cpp and once for zenohd), and so that the router could be more easily integrated into the ROS 2 package format.

As of 2024-02-09, the user is expected to launch the router by hand.
In order to integrate more seamlessly into ROS 2, the Zenoh router can be launched by running `ros2 run rmw_zenoh_cpp rmw_zenohd`.
Expand Down Expand Up @@ -120,7 +120,7 @@ N/A

Zenoh inherently allows any pub/sub or querier/queryable pair to communicate, irrespective of their data type, encoding, or QoS, as long as their key expressions match.

In contrast, to ensure rmw_zenoh_cpp aligns with the behavior of other RMW implementations, its key expressions have been specifically designed to
In contrast, to ensure `rmw_zenoh_cpp` aligns with the behavior of other RMW implementations, its key expressions have been specifically designed to

* Prevent any communication between Sessions using different `ROS_DOMAIN_ID`, even when they are connecting to the same Zenoh infrastructure (i.e., interconnected routers).
* Prevent any communication between Publishers and Subscribers that use the same topic name but have different type names or type definitions. This also applies to Service Servers and Clients.
Expand Down Expand Up @@ -196,7 +196,7 @@ Where:
* `<entity_kind>` - The kind of the entity. Possible values:
* `NN` for a node
* `MP` for a message publisher
* `MS` for a message subscriber
* `MS` for a message subscription
* `SS` for a service server
* `SC` for a service client
* `<mangled_enclave>` - The mangled SROS enclave name (just `%` if not set)
Expand All @@ -207,7 +207,7 @@ Where:
* `<type_hash>` - the topic or service type hash, as defined in [REP-2016](https://github.com/ros-infrastructure/rep/pull/381/files) and generated by [`rosidl`](https://github.com/ros2/rosidl/tree/rolling/rosidl_generator_type_description)
* `<qos>` - The entity QoS encoded in a compact format (see the `qos_to_keyexpr` function)

During context initialization, `rmw_zenoh_cpp` calls `zc_liveliness_get` to get an initial view of the entire graph from other nodes in the system.
During context initialization, `rmw_zenoh_cpp` calls `Session::liveliness_get` to get an initial view of the entire graph from other nodes in the system.
From then on, when entities enter and leave the system, `rmw_zenoh_cpp` will get new liveliness tokens that it can use to update its internal cache.

Examples using the demo_nodes_cpp package:
Expand All @@ -218,7 +218,7 @@ Examples using the demo_nodes_cpp package:
@ros2_lv/2/aac3178e146ba6f1fc6e6a4085e77f21/0/0/NN/%/%/listener
```

* `listener` node's subscriber on `chatter` topic:
* `listener` node's subscription on `chatter` topic:

```text
@ros2_lv/2/aac3178e146ba6f1fc6e6a4085e77f21/0/10/MS/%/%/listener/%chatter/std_msgs::msg::dds_::String_/RIHS01_df668c740482bbd48fb39d76a70dfd4bd59db1288021743503259e948f6b1a18/::,10:,:,:,,
Expand Down Expand Up @@ -340,8 +340,8 @@ When a new node is created through the RMW API, a liveliness token of type `NN`

A ROS 2 publisher sends data to 0 or more connected subscriptions.
A Zenoh publisher does exactly the same thing, so ROS 2 publishers are mapped onto Zenoh publishers in `rmw_zenoh_cpp`.
The Zenoh advanced publisher API is used to benefit of some features that are implemented on top of a regular Zenoh publisher.
For instance, if the Quality of Service durability for a publisher is `TRANSIENT_LOCAL`, the Zenoh advanced publisher will be created with a publication cache.
For certain Quality of Service features, the Zenoh advanced publisher API is used.
For instance, if the Quality of Service durability for a publisher is `TRANSIENT_LOCAL`, a Zenoh advanced publisher will be created with a publication cache.
See the [Quality of Service](#Quality-of-Service) section below for more information.

When a new publisher is created, a liveliness token of type `MP` is sent out.
Expand Down Expand Up @@ -381,8 +381,8 @@ A ROS 2 Publisher publishes messages calling the Zenoh `put` operation with:

A ROS 2 subscription receives data from 1 or more connected publishers.
A Zenoh subscriber does exactly the same thing, so ROS 2 subscriptions are mapped onto Zenoh subscribers in `rmw_zenoh_cpp`.
The Zenoh advanced subscriber API is used to benefit of some features that are implemented on top of a regular Zenoh publisher.
For instance, if the Quality of Service durability for a subscription is `TRANSIENT_LOCAL`, the Zenoh advanced publisher will be created with options to query the historical data cached by the advanced publishers.
For certain Quality of Service features, the Zenoh advanced subscriber API is used.
For instance, if the Quality of Service durability for a subscription is `TRANSIENT_LOCAL`, a Zenoh advanced publisher will be created with options to query the historical data cached by the advanced publishers.
See the [Quality of Service](#Quality-of-Service) section below for more information.

When new data arrives, a callback within `rmw_zenoh_cpp` is executed, which takes ownership of the data and signals that there is data available.
Expand Down Expand Up @@ -471,10 +471,10 @@ When a new service client is created, a liveliness token of type `SC` is sent ou
In ROS 2, services are meant to be used for remote procedure calls that will return fairly quickly.
`rmw_zenoh_cpp` uses Zenoh queryables to implement ROS 2 services.
When a ROS 2 node wants to advertise a service to the network, it calls `rmw_create_service`.
`rmw_zenoh_cpp` uses the `declare_queryable` Zenoh API to create that service. The queryable is delared with the key expression mapped from the service name. As this key expression doesn't contain any wildcard (`*` or `**`) the queryable is declared as complete (i.e. it can send a reply for every request).
`rmw_zenoh_cpp` uses the `declare_queryable` Zenoh API to create that service. The queryable is declared with the key expression mapped from the service name. As this key expression doesn't contain wildcards (`*` or `**`) the queryable is declared as complete (i.e. it can send a reply for every request).

When a client request comes in, `rmw_take_request` is called to send the query to the user callback, which should perform some computation.
Once the user callback returns, `rmw_send_response` is called to send the response back to the requester. This response is sent calling the Zenoh `reply` operation with:
Once the user callback returns, `rmw_send_response` is called to send the response back to the requester. This response is sent using the Zenoh `reply` operation with:

* the serialized response
* an attachment containing the sequence number received in the request attachment, a source timestamp for the reply and the client GID received in the request attachment. It is encoded as such:
Expand Down Expand Up @@ -513,26 +513,26 @@ When a new service server is created, a liveliness token of type `SS` is sent ou
## Quality of Service

The ROS 2 RMW layer defines quite a few Quality of Service settings that are largely derived from DDS.
Here is an incomplete list of some of the settings and the values that they can take:

* RELIABILITY
* RELIABLE - Applicable only for publishers. Data delivery via a reliable transport (e.g. `tcp` or `quic`), if such endpoints are configured.
* BEST_EFFORT - Data may be dropped during delivery. If non-reliable endpoints are configured (e.g. `udp`) they will be used. Otherwise reliable transport will be used. Note that with default configuration, only TCP transport are used. This is the `SYSTEM_DEFAULT` reliability.
* HISTORY
* KEEP_LAST - For subscriptions, only keep up to a maximum number of samples (defined by depth); once the maximum is reached, older samples will be lost. For publications, if TRANSIENT_LOCAL is set, the DEPTH will define the size of the publication cache. This is the `SYSTEM_DEFAULT` history.
* KEEP_ALL - For subscriptions, keep all values. For publication, if RELIABLE, the `CongestionControl::BLOCK` mode is set, meaning the publisher will be blocked when network congestion occurs. This leads a periodic publisher to adapt its publication rate to what the network or the subscriptions can handle.
* DEPTH - The maximum number of samples to keep; only comes into play when KEEP_LAST history is used. If `DEPTH` is set to 0, `rmw_zenoh_cpp` will choose a depth of 42.
* DURABILITY
* VOLATILE - Samples will only be delivered to subscriptions that are active at the time of publishing. This is the `SYSTEM_DEFAULT` durability.
* TRANSIENT_LOCAL - "Late-joining" subscriptions will receive historical data, along with any new data. In `rmw_zenoh_cpp`, this is implemented for publications via the activation of a cache on the `AdvancedPublisher`. On the subscription side the `AdvancedSubscriber` is configured to query this cache to retrieve the historical data.
* LIVELINESS
* AUTOMATIC - The "liveliness" of an entity of the system is managed by the RMW layer. This is the only LIVELINESS that `rmw_zenoh_cpp` supports.
* MANUAL_BY_TOPIC - No supported. It is up to the application to periodically publish to a particular topic to assert liveliness.
* DEADLINE - The period at which messages are expected to be sent/received. Currently unimplemented in `rmw_zenoh_cpp`.
* LIFESPAN - The age at which messages are expired and no longer valid. Currently unimplemented in `rmw_zenoh_cpp`.
Here is an incomplete list of some of the settings and the values that they can take, along with some details on how they are mapped in `rmw_zenoh_cpp`:

* `RELIABILITY`
* `RELIABLE` - Applicable only for publishers. Data delivery is via a reliable transport (e.g. `tcp` or `quic`), if such endpoints are configured.
* `BEST_EFFORT` - Data may be dropped during delivery. If non-reliable endpoints are configured (e.g. `udp`) they will be used. Otherwise a reliable transport will be used. Note that with the default configuration, only the TCP transport is configured. This is also the `SYSTEM_DEFAULT` reliability.
* `HISTORY`
* `KEEP_LAST` - For subscriptions, only keep up to a maximum number of samples (defined by `DEPTH`); once the maximum is reached, older samples will be lost. For publications, if `TRANSIENT_LOCAL` is set, the `DEPTH` will define the size of the publication cache. This is the `SYSTEM_DEFAULT` history.
* `KEEP_ALL` - For subscriptions, keep all values. For publishers, if the `RELIABILITY` is `RELIABLE`, the `CongestionControl::BLOCK` mode is set, meaning the publisher will be blocked when network congestion occurs. This allows a periodic publisher to adapt its publication rate to what the network or the subscriptions can handle.
* `DEPTH` - The maximum number of samples to keep; this only comes into play when `KEEP_LAST` history is used. If `DEPTH` is set to 0, `rmw_zenoh_cpp` will choose a depth of 42.
* `DURABILITY`
* `VOLATILE` - Samples will only be delivered to subscriptions that are active at the time of publishing. This is the `SYSTEM_DEFAULT` durability.
* `TRANSIENT_LOCAL` - "Late-joining" subscriptions will receive historical data, along with any new data. In `rmw_zenoh_cpp`, this is implemented for publishers via the activation of a cache on the `AdvancedPublisher`. On the subscription side the `AdvancedSubscriber` is configured to query this cache to retrieve the historical data.
* `LIVELINESS`
* `AUTOMATIC` - The "liveliness" of an entity of the system is managed by the RMW layer. This is the only `LIVELINESS` that `rmw_zenoh_cpp` supports.
* `MANUAL_BY_TOPIC` - Not supported. It is up to the application to periodically publish to a particular topic to assert liveliness.
* `DEADLINE` - The period at which messages are expected to be sent/received. Currently unimplemented in `rmw_zenoh_cpp`.
* `LIFESPAN` - The age at which messages are expired and no longer valid. Currently unimplemented in `rmw_zenoh_cpp`.

In Zenoh, there are essentially no "incompatible" Quality of Service settings.
This means that any publisher can match any subscriber.
This means that any publisher can match any subscription.

### Related RMW APIs

Expand All @@ -555,18 +555,18 @@ For instance, if a message is lost, then the RMW layer may raise an event to the
Events are broken down into subscription events and publisher events:

* Subscription
* LIVELINESS_CHANGED
* DEADLINE_MISSED
* QOS_INCOMPATIBLE
* MESSAGE_LOST
* INCOMPATIBLE_TYPE
* MATCHED
* `LIVELINESS_CHANGED`
* `DEADLINE_MISSED`
* `QOS_INCOMPATIBLE`
* `MESSAGE_LOST`
* `INCOMPATIBLE_TYPE`
* `MATCHED`
* Publisher
* LIVELINESS_LOST
* DEADLINE_MISSED
* QOS_INCOMPATIBLE
* INCOMPATIBLE_TYPE
* MATCHED
* `LIVELINESS_LOST`
* `DEADLINE_MISSED`
* `QOS_INCOMPATIBLE`
* `INCOMPATIBLE_TYPE`
* `MATCHED`

### Related RMW APIs

Expand All @@ -584,7 +584,7 @@ N/A
## Actions

As of 2024-02-09, there is no concept of an action at the RMW level in ROS 2.
Instead, actions are composed of several services and pub/sub.
Instead, actions are composed of several services and publishers/subcriptions.
Thus, there is no direct implementation of actions in `rmw_zenoh_cpp`.

## Security
Expand Down