asterism-zenoh 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +89 -0
- data/README.md +145 -13
- data/docs/feature_coverage.md +55 -0
- data/ext/asterism_zenoh/zenoh.c +3562 -785
- data/lib/asterism/zenoh/common.rb +221 -0
- data/lib/asterism/zenoh/cruby.rb +128 -0
- data/lib/asterism/zenoh/values.rb +112 -0
- data/lib/asterism/zenoh/version.rb +1 -1
- data/lib/asterism/zenoh.rb +7 -1
- metadata +12 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d0a06884bbb4ef08eebe9eab69518c1d4c300b345ad7853742cda7ae48175e8d
|
|
4
|
+
data.tar.gz: acb609412809b6324c07d2a874819dfffa6499aec3b85d75e9c764bee551e2c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 861adc9617255ca81225f4782e36c6fc664a37e56c34f778efebd6890359380ca047d9b9f29bc59aa12ddba39bee10f188cf3317244490bdc0299590a93e0e72
|
|
7
|
+
data.tar.gz: 75075726af74d8d6bfa6b785ac53b462e805f3445e9a434c94f5d38d07b1839d46493751c584e4c957f771b66ca36e76e11e2ee80bdec718340480c66b36279b
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to asterism-zenoh. The mruby / PicoRuby binding
|
|
4
|
+
(picoruby-asterism-zenoh) carries the same version number from 0.4.0 on.
|
|
5
|
+
Nothing has been published to rubygems.org yet; the versions below are
|
|
6
|
+
the ones in this repository.
|
|
7
|
+
|
|
8
|
+
## 0.4.0
|
|
9
|
+
|
|
10
|
+
The first step of the API review toward 1.0 (the asterism gem's
|
|
11
|
+
docs/api_review.md). Additions and deprecations only: every 0.3.0 call
|
|
12
|
+
works as before.
|
|
13
|
+
|
|
14
|
+
Added
|
|
15
|
+
|
|
16
|
+
- Time limits in seconds: `timeout:` on `get`, `liveliness_get` and
|
|
17
|
+
`querier`, `query_timeout:` on `advanced_subscriber`, and
|
|
18
|
+
`connect_timeout:` on `Session.open` (it sets `connect/timeout_ms`).
|
|
19
|
+
`timeout_ms:` stays next to `timeout:`. Giving a time limit twice raises
|
|
20
|
+
`ArgumentError`, as does `scout(timeout:, timeout_ms:)`.
|
|
21
|
+
- Keywords next to the positional optionals: `get(key, params:, payload:)`,
|
|
22
|
+
`depth:` on `subscribe`, `queryable`, `liveliness_watch`,
|
|
23
|
+
`advanced_subscriber`, the listeners and the event streams, and
|
|
24
|
+
`Querier#get(params:, payload:)`.
|
|
25
|
+
- `depth:` on `get`, `liveliness_get` and `Querier#get`: the replies kept
|
|
26
|
+
until taken. Before, a get's queue held 16 and could not be changed, so
|
|
27
|
+
the replies to a wildcard past the 16th were dropped (a router answers
|
|
28
|
+
in one burst; `liveliness_get("@ros2_lv/**")` returned 16 of 31).
|
|
29
|
+
- `DEFAULT_DEPTH` (16), `DEFAULT_GET_DEPTH`, `DEFAULT_WATCH_DEPTH` and
|
|
30
|
+
`MAX_DEPTH`.
|
|
31
|
+
- `Asterism.warn_once(obj, message)`: warns once per object (the asterism
|
|
32
|
+
gem uses it for gets and watches that dropped something).
|
|
33
|
+
- `Session#connection_count` (the routers of a client session, or the peers
|
|
34
|
+
of a peer session).
|
|
35
|
+
- One error tree: `Asterism::Error` (defined here, reopened by the asterism
|
|
36
|
+
gem) > `Asterism::Zenoh::Error` > `Asterism::Zenoh::ClosedError` (the
|
|
37
|
+
session is closed or its connection was lost). `Error#code` is zenoh-c's
|
|
38
|
+
result code when the failure had one.
|
|
39
|
+
- `Session.open { |s| }` closes the session after the block.
|
|
40
|
+
- `Query#reply_error` / `reply_delete` (the spelled-out `reply_err` /
|
|
41
|
+
`reply_del`).
|
|
42
|
+
- `BACKEND` (`:zenoh_c`), `BACKEND_VERSION`, `PEER_SUPPORTED`,
|
|
43
|
+
`DEFAULT_TIMEOUT` (2.0 s).
|
|
44
|
+
- `express?`, `multicast?` and `streamed?` on the values that have those
|
|
45
|
+
members.
|
|
46
|
+
- A session used in a forked child raises `ClosedError` ("open a new
|
|
47
|
+
session after fork") instead of hanging on zenoh-c's threads, which do
|
|
48
|
+
not survive `fork`.
|
|
49
|
+
- `Asterism.deprecated` and `Asterism.deprecations = :warn / :raise /
|
|
50
|
+
:silent` (also `ASTERISM_DEPRECATIONS`): the one helper every Asterism
|
|
51
|
+
gem uses to warn once per name.
|
|
52
|
+
|
|
53
|
+
Changed
|
|
54
|
+
|
|
55
|
+
- The default queue depth of `get`, `liveliness_get`, `Querier#get` and
|
|
56
|
+
`liveliness_watch` is 1024 (was 16), so a burst of replies or of live
|
|
57
|
+
tokens is kept whole. The largest `depth:` is 65536 (was 1024). The
|
|
58
|
+
queues grow as entries come, so a deep one costs nothing while empty.
|
|
59
|
+
|
|
60
|
+
Deprecated (each warns once; removed or changed in 1.0)
|
|
61
|
+
|
|
62
|
+
- The time limit as a positional argument: `get(key, timeout_ms, params,
|
|
63
|
+
payload)` and `liveliness_get(key, timeout_ms)`. A Float there warns
|
|
64
|
+
with its own message: it is milliseconds and truncated (`get(key, 2.0)`
|
|
65
|
+
waits 2 ms).
|
|
66
|
+
- `Session#peers`: use `connection_count`.
|
|
67
|
+
- `Query#reply(payload)` when the query's key differs from the queryable's
|
|
68
|
+
own key and that key has no wildcard: from 1.0 such a reply answers on
|
|
69
|
+
the queryable's key.
|
|
70
|
+
|
|
71
|
+
## 0.3.0
|
|
72
|
+
|
|
73
|
+
- Configuration (`config:` Hash or JSON5 String, `config_file:`; TLS and
|
|
74
|
+
the rest of zenoh's keys), scouting, `delete`, declared publishers and
|
|
75
|
+
queriers with matching status, the full sample and reply values (`Data`),
|
|
76
|
+
error and delete replies, encodings, timestamps, key expressions, the
|
|
77
|
+
advanced publisher / subscriber, transport and link events, zenoh-c's
|
|
78
|
+
log. All CRuby only.
|
|
79
|
+
- CI on Linux and macOS, Ruby 3.2 to 4.0, and an installed-gem check.
|
|
80
|
+
|
|
81
|
+
## 0.2.0
|
|
82
|
+
|
|
83
|
+
- A session and its objects may be used from several Ruby threads.
|
|
84
|
+
|
|
85
|
+
## 0.1.0
|
|
86
|
+
|
|
87
|
+
- The CRuby binding over the prebuilt zenoh-c, split out of the asterism
|
|
88
|
+
repository: the API of the mruby / PicoRuby binding (sessions, put /
|
|
89
|
+
subscribe, get / queryable, liveliness, attachments).
|
data/README.md
CHANGED
|
@@ -18,6 +18,28 @@ loop do
|
|
|
18
18
|
end
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
## Feature coverage
|
|
22
|
+
|
|
23
|
+
Supported: sessions (client, peer, listening, multicast scouting), any
|
|
24
|
+
zenoh configuration (a Hash of keys, a JSON5 String or file: TLS, QUIC,
|
|
25
|
+
WebSocket, authentication, timeouts), put / delete with encoding, priority,
|
|
26
|
+
congestion control, express, reliability and timestamps, declared
|
|
27
|
+
publishers and queriers with matching status, subscribers with the full
|
|
28
|
+
sample (kind, encoding, timestamp, ...), get / queryable with error and
|
|
29
|
+
delete replies, liveliness, the advanced publisher / subscriber (history
|
|
30
|
+
for late subscribers, recovery, publisher detection; ROS 2 transient
|
|
31
|
+
local), transport and link events, key expression operations and declared
|
|
32
|
+
key expressions, HLC timestamps, zenoh-c's log.
|
|
33
|
+
|
|
34
|
+
Not exposed: shared memory, background declarations, zenoh's own
|
|
35
|
+
serializer (MessagePack and CDR are used instead), the publication cache /
|
|
36
|
+
querying subscriber (the older form of the advanced ones), cancelling a
|
|
37
|
+
get.
|
|
38
|
+
|
|
39
|
+
The table, with the reasons and what the boards' zenoh-pico could offer:
|
|
40
|
+
[docs/feature_coverage.md](docs/feature_coverage.md). The calls added in
|
|
41
|
+
0.3.0 are CRuby only.
|
|
42
|
+
|
|
21
43
|
## API
|
|
22
44
|
|
|
23
45
|
The same calls, arguments and results as the mruby / PicoRuby gem
|
|
@@ -25,25 +47,114 @@ The same calls, arguments and results as the mruby / PicoRuby gem
|
|
|
25
47
|
|
|
26
48
|
| Call | Notes |
|
|
27
49
|
|---|---|
|
|
28
|
-
| `Session.open(locator = nil, mode: :client, listen: nil)` | client of a router, or `mode: :peer` connecting to `locator` and/or listening on `listen:`. `Error` when nobody answers within `CONNECT_TIMEOUT_MS`. Releases the GVL while connecting |
|
|
50
|
+
| `Session.open(locator = nil, mode: :client, listen: nil) { \|s\| }` | client of a router, or `mode: :peer` connecting to `locator` and/or listening on `listen:`. `Error` when nobody answers within `CONNECT_TIMEOUT_MS`. With a block: closes the session after it and returns the block's value. Releases the GVL while connecting |
|
|
29
51
|
| `session.put(key, payload, attachment: nil)` | `payload` / `attachment` are Strings (bytes). Releases the GVL |
|
|
30
|
-
| `session.subscribe(key, depth
|
|
31
|
-
| `session.get(key,
|
|
32
|
-
| `session.queryable(key, depth
|
|
33
|
-
| `q.key` / `params` / `payload` / `attachment`, `q.reply(
|
|
52
|
+
| `session.subscribe(key, depth: 16)` -> `Subscriber` | `each_pending { \|key, payload, attachment\| }` (or an Array), `pending` / `received` / `dropped`, `close` / `closed?`. `depth` may also be positional |
|
|
53
|
+
| `session.get(key, timeout: 2.0, params: nil, payload: nil, attachment: nil, target: :all, consolidation: :none, depth: DEFAULT_GET_DEPTH)` -> `Get` | `timeout:` in seconds, or `timeout_ms:`. `depth:` the replies kept until taken (see Queue depths). Returns at once; `each_reply { \|key, payload, attachment\| }`, `done?`, `pending` / `received` / `dropped` / `errors`. `consolidation: :none` (every reply) differs from zenoh's `:auto` on purpose: services and the object layer want every reply |
|
|
54
|
+
| `session.queryable(key, depth: 16, complete: false)` -> `Queryable` | `each_pending { \|q\| }` (each query finished after the block) or an Array of `Query` |
|
|
55
|
+
| `q.key` / `params` / `payload` / `attachment`, `q.reply(key, payload, attachment: nil)`, `q.finish` / `finished?` | `q.reply(payload)` answers on the query's key (see Deprecations) |
|
|
34
56
|
| `session.liveliness(key)` -> `LivelinessToken` | `close` / `closed?` |
|
|
35
|
-
| `session.liveliness_watch(key, depth
|
|
36
|
-
| `session.liveliness_get(key,
|
|
37
|
-
| `session.poll(steps = 8)` / `closed?` / `close` / `zid` / `
|
|
38
|
-
| `Asterism::Zenoh::Error` | |
|
|
39
|
-
| `CONNECT_TIMEOUT_MS`, `SEND_TIMEOUT_MS` (3000), `PEER` (true), `MAX_PEERS`, `C_VERSION` | `MAX_PEERS` is zenoh-c's `transport/unicast/max_sessions` (1000);
|
|
57
|
+
| `session.liveliness_watch(key, depth: DEFAULT_WATCH_DEPTH)` -> `LivelinessWatch` | `each_pending { \|key, alive\| }`, `pending` / `received` / `dropped`; the tokens alive now come first, in one burst |
|
|
58
|
+
| `session.liveliness_get(key, timeout: 2.0, depth: DEFAULT_GET_DEPTH)` -> `Get` | or `timeout_ms:` |
|
|
59
|
+
| `session.poll(steps = 8)` / `closed?` / `close` / `zid` / `connection_count` | `connection_count`: the routers (client) or peers (peer mode) connected now |
|
|
60
|
+
| `Asterism::Error` > `Asterism::Zenoh::Error` > `Asterism::Zenoh::ClosedError` | `ClosedError`: the session is closed or its connection was lost. `Error#code`: zenoh-c's result code when there was one |
|
|
61
|
+
| `CONNECT_TIMEOUT_MS`, `SEND_TIMEOUT_MS` (3000), `PEER` / `PEER_SUPPORTED` (true), `MAX_PEERS`, `BACKEND` (`:zenoh_c`), `BACKEND_VERSION` (= `C_VERSION`), `DEFAULT_TIMEOUT` (2.0 s), `DEFAULT_DEPTH` (16), `DEFAULT_GET_DEPTH` / `DEFAULT_WATCH_DEPTH` (1024), `MAX_DEPTH` (65536), `VERSION` | `MAX_PEERS` is zenoh-c's `transport/unicast/max_sessions` (1000); on the boards `BACKEND` is `:zenoh_pico` |
|
|
62
|
+
| `Asterism.deprecations = :warn / :raise / :silent` | how deprecated calls are reported (below) |
|
|
63
|
+
|
|
64
|
+
Units: a keyword without a unit suffix is seconds (`timeout:`,
|
|
65
|
+
`connect_timeout:`, `query_timeout:`); anything else carries its unit
|
|
66
|
+
(`timeout_ms:`, `CONNECT_TIMEOUT_MS`). Giving a time limit twice
|
|
67
|
+
(`timeout:` and `timeout_ms:`) raises `ArgumentError`.
|
|
40
68
|
|
|
41
69
|
Keys come back as UTF-8 Strings, payloads and attachments as binary
|
|
42
70
|
(ASCII-8BIT) Strings.
|
|
43
71
|
|
|
72
|
+
### Added in 0.3.0 (CRuby only)
|
|
73
|
+
|
|
74
|
+
Every 0.2.0 call works as before; these are new keywords and methods.
|
|
75
|
+
|
|
76
|
+
| Call | Notes |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `Session.open(locator = nil, mode:, listen:, scouting:, timestamping:, config:, config_file:, connect_timeout:)` | `config:` a Hash (`{"transport/link/tls/root_ca_certificate" => "ca.pem"}`, Ruby values sent as JSON) or a JSON5 String; `config_file:` a JSON5 file. Lowest first: zenoh's defaults, the gem's own settings (no scouting, the time limits), the file / String, the arguments, the Hash. `scouting: true` needs no locator. `connect_timeout:` (seconds, 0.4.0) sets `connect/timeout_ms` |
|
|
79
|
+
| `Asterism::Zenoh.scout(what: [:router, :peer], timeout: 1.0, config: nil)` (or `timeout_ms:`) | Array of `Hello` (`zid`, `whatami`, `locators`) |
|
|
80
|
+
| `session.put(key, payload, attachment:, encoding:, priority:, congestion_control:, express:, reliability:, timestamp:, allowed_destination:)` | `priority:` `:real_time` .. `:background` (or 1..7), `congestion_control:` `:drop` / `:block` / `:block_first`, `reliability:` `:reliable` / `:best_effort`, `timestamp:` `true` or a `Timestamp`, `allowed_destination:` `:any` / `:remote` / `:session_local` |
|
|
81
|
+
| `session.delete(key, ...)` | the same options without payload, attachment and encoding |
|
|
82
|
+
| `session.publisher(key, encoding:, priority:, ...)` -> `Publisher` | `put(payload, attachment:, encoding:, timestamp:)`, `delete(timestamp:)`, `matching?`, `matching_listener(depth: 16)`, `close` / `closed?` |
|
|
83
|
+
| `session.querier(key, target:, consolidation:, timeout: 2.0, ...)` -> `Querier` | `timeout:` seconds or `timeout_ms:`; `get(params: nil, payload: nil, attachment:, encoding:, depth:)` -> `Get`, `matching?`, `matching_listener`, `close` |
|
|
84
|
+
| `sub.each_sample { \|sample\| }` | `Sample` (`key`, `payload`, `attachment`, `kind`, `encoding`, `timestamp`, `priority`, `congestion_control`, `express`, `reliability`, `source_zid`); same queue as `each_pending` |
|
|
85
|
+
| `get.each_result { \|reply\| }` | `Reply` (`ok?` / `error?`, `key`, `payload`, `encoding`, `kind`, `timestamp`, `replier_zid`); error replies included. `each_reply` still leaves them out |
|
|
86
|
+
| `session.get(..., encoding:, priority:, congestion_control:, express:, accept_replies:)` | |
|
|
87
|
+
| `q.reply(..., encoding:, timestamp:, priority:, congestion_control:, express:)`, `q.reply_error(payload, encoding:)`, `q.reply_delete(key = nil)`, `q.encoding` | `reply_err` / `reply_del` are the same (kept) |
|
|
88
|
+
| `session.advanced_publisher(key, cache:, sample_miss_detection:, publisher_detection:, ...)` -> `AdvancedPublisher` | as `Publisher`. `cache: N` keeps the last N samples for late subscribers |
|
|
89
|
+
| `session.advanced_subscriber(key, depth: 16, history:, recovery:, subscriber_detection:, query_timeout:)` -> `AdvancedSubscriber` | as `Subscriber`, plus `detect_publishers` (a `LivelinessWatch`) and `miss_listener` (`Miss`: `source_zid`, `source_eid`, `count`) |
|
|
90
|
+
| `session.transport_events(depth: 16, history: false)`, `session.link_events(...)` -> `EventListener` | `each_pending` gives `TransportEvent` / `LinkEvent` (`kind` `:added` / `:removed`, `zid`, ...) |
|
|
91
|
+
| `session.peer_zids`, `router_zids`, `transports`, `links` | the IDs, `Transport` and `Link` values connected now |
|
|
92
|
+
| `session.new_timestamp` -> `Timestamp` | `ntp64`, `id`, `to_time`, Comparable. From the session's HLC with `timestamping: true` (strictly increasing); otherwise from the system clock, so two in a row may be equal |
|
|
93
|
+
| `Asterism::Zenoh::KeyExpr.new(str, autocanonize: false)` | `intersects?`, `includes?`, `relation_to` (`:disjoint` / `:intersects` / `:includes` / `:equals`), `join`, `concat`, `==`; `KeyExpr.canonize(str)`, `KeyExpr.valid?(str)`. Accepted wherever a key String is |
|
|
94
|
+
| `session.declare_keyexpr(key)` -> `KeyExpr` | declared on the session (sent as a number afterwards); `undeclare` |
|
|
95
|
+
| `Asterism::Zenoh.init_log(level = nil)` | zenoh-c's log on standard output (`"info"`, `"debug"`, or a filter); `RUST_LOG` wins |
|
|
96
|
+
|
|
97
|
+
Listeners (`MatchingListener`, `EventListener`) are polled like the
|
|
98
|
+
subscribers: `each_pending` (yields or returns an Array), `pending`,
|
|
99
|
+
`received`, `dropped`, `close` / `closed?`. The values are `Data` objects
|
|
100
|
+
(`lib/asterism/zenoh/values.rb`), so they work with pattern matching; the
|
|
101
|
+
boolean members also have predicates (`express?`, `multicast?`,
|
|
102
|
+
`streamed?`).
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
Z = Asterism::Zenoh
|
|
106
|
+
s = Z::Session.open("tls/192.0.2.2:7447",
|
|
107
|
+
config: { "transport/link/tls/root_ca_certificate" => "ca.pem" })
|
|
108
|
+
pub = s.publisher("demo/temp", encoding: "text/plain", priority: :data_high)
|
|
109
|
+
watch = pub.matching_listener
|
|
110
|
+
sub = s.subscribe("demo/**")
|
|
111
|
+
loop do
|
|
112
|
+
watch.each_pending { |listening| puts "listened to: #{listening}" }
|
|
113
|
+
pub.put("21.5", timestamp: true)
|
|
114
|
+
sub.each_sample do |sm|
|
|
115
|
+
case sm
|
|
116
|
+
in {kind: :delete, key:} then puts "#{key} deleted"
|
|
117
|
+
in {encoding: "application/json", payload:} then p payload
|
|
118
|
+
else puts "#{sm.key} at #{sm.timestamp&.to_time}"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
sleep 0.1
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# A late subscriber gets the last values (ROS 2's transient local works this way)
|
|
125
|
+
latched = s.advanced_publisher("demo/mode", cache: 1, sample_miss_detection: true)
|
|
126
|
+
latched.put("eco")
|
|
127
|
+
late = s.advanced_subscriber("demo/mode", history: true)
|
|
128
|
+
|
|
129
|
+
Z.scout(what: :peer, timeout: 1.0).each { |h| puts "#{h.zid} #{h.locators}" }
|
|
130
|
+
Z::KeyExpr.new("demo/*").includes?("demo/temp") # => true
|
|
131
|
+
```
|
|
132
|
+
|
|
44
133
|
`require "asterism/zenoh/global"` defines `Zenoh = Asterism::Zenoh` for
|
|
45
134
|
those who want the short name; nothing defines it by default.
|
|
46
135
|
|
|
136
|
+
## Deprecations (0.4.0) and what 1.0 changes
|
|
137
|
+
|
|
138
|
+
0.4.0 only adds; every 0.3.0 call still works. The old forms below warn
|
|
139
|
+
once per name (`warn`; on the boards `puts` when there is no `warn`).
|
|
140
|
+
`Asterism.deprecations = :raise` (or `ASTERISM_DEPRECATIONS=raise` in the
|
|
141
|
+
environment) raises `Asterism::DeprecationError` instead, which is what
|
|
142
|
+
the tests and CI use; `:silent` turns the warnings off.
|
|
143
|
+
|
|
144
|
+
| Deprecated | Use | 1.0 |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| `get(key, timeout_ms, params, payload)` (the time as a positional argument) | `get(key, timeout: 2.0, params:, payload:)` or `timeout_ms:` | removed (positional depth stays) |
|
|
147
|
+
| a Float there (`get(key, 2.0)` waits 2 ms) | `timeout: 2.0` | removed; warns with its own message now |
|
|
148
|
+
| `liveliness_get(key, timeout_ms)` | `liveliness_get(key, timeout: 1.0)` | removed |
|
|
149
|
+
| `session.peers` | `session.connection_count` | removed |
|
|
150
|
+
| `q.reply(payload)` on a query whose key differs from the queryable's own plain key | `q.reply(key, payload)` | answers on the queryable's own key when it has no wildcard (as the CRuby block API of the `asterism` gem does) |
|
|
151
|
+
|
|
152
|
+
Thread-safety: a `Session` and everything declared on it may be used from
|
|
153
|
+
several threads (below); each queued entry goes to exactly one taker. A
|
|
154
|
+
session opened before `fork` is unusable in the child: its calls raise
|
|
155
|
+
`ClosedError` ("open a new session after fork"); the parent's session is
|
|
156
|
+
not touched. Ractors are not supported.
|
|
157
|
+
|
|
47
158
|
## How receiving works
|
|
48
159
|
|
|
49
160
|
zenoh-c runs the protocol on its own threads. Every subscriber, liveliness
|
|
@@ -67,18 +178,39 @@ thread and Enumerators on top of this API are in the CRuby layer of the
|
|
|
67
178
|
|
|
68
179
|
## Behaviour kept from the mruby gem
|
|
69
180
|
|
|
70
|
-
- **No scouting
|
|
71
|
-
listen.
|
|
181
|
+
- **No scouting** unless asked for (`scouting: true`): the locator is
|
|
182
|
+
given. A peer that only connects does not listen.
|
|
72
183
|
- **Remote only**: a session's own puts do not reach its own subscribers,
|
|
73
184
|
and its gets do not reach its own queryables (zenoh-pico's behaviour;
|
|
74
185
|
Asterism calls its own objects in place).
|
|
75
186
|
- **Losing the connection**: a client session is closed when it has no
|
|
76
187
|
router left, a peer session that only connects when it has no peer left;
|
|
77
|
-
a listening session stays open. From then on `poll` is false, `closed?`
|
|
188
|
+
a listening (or scouting) session stays open. From then on `poll` is false, `closed?`
|
|
78
189
|
true, and `put` raises `Asterism::Zenoh::Error`. No reconnection.
|
|
79
190
|
- **Full queues drop the oldest** entry and count it in `dropped` (a dropped
|
|
80
191
|
query is finished, so its requester gets no answer from it).
|
|
81
192
|
|
|
193
|
+
### Queue depths
|
|
194
|
+
|
|
195
|
+
Every receiving object has a bounded queue; what does not fit is dropped,
|
|
196
|
+
oldest first, and counted in `dropped`. A router answers a wildcard get or
|
|
197
|
+
liveliness get, and a new liveliness watch, with everything at once, so
|
|
198
|
+
those queues are deep by default:
|
|
199
|
+
|
|
200
|
+
| | CRuby (zenoh-c) | Boards (zenoh-pico) |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| `subscribe`, `queryable`, listeners (`DEFAULT_DEPTH`) | 16 | 16 |
|
|
203
|
+
| `get`, `liveliness_get`, `querier.get` (`DEFAULT_GET_DEPTH`) | 1024 | 16 |
|
|
204
|
+
| `liveliness_watch` (`DEFAULT_WATCH_DEPTH`) | 1024 | 16 |
|
|
205
|
+
| largest `depth:` (`MAX_DEPTH`) | 65536 | 1024 |
|
|
206
|
+
|
|
207
|
+
On a PC a deep queue costs nothing until it fills (zenoh-c's FIFO and the
|
|
208
|
+
gem's lists grow as entries come). On a board a get's queue is allocated
|
|
209
|
+
when the get is sent; pass `depth:` when a wildcard can match more than 16
|
|
210
|
+
answers. Check `dropped` after a get whose answers matter; the asterism
|
|
211
|
+
gem's CRuby API warns once when one of its gets or watches dropped
|
|
212
|
+
something (`Asterism.warn_once`).
|
|
213
|
+
|
|
82
214
|
Differences: `C_VERSION` instead of `PICO_VERSION`; `MAX_PEERS` is zenoh-c's
|
|
83
215
|
limit, not 3; the time limits of gets are kept by zenoh-c (exact, not
|
|
84
216
|
checked once a second); liveliness watches may also report the session's
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# zenoh-c feature coverage of asterism-zenoh
|
|
2
|
+
|
|
3
|
+
Which zenoh-c 1.10.1 features the Ruby binding exposes (asterism-zenoh 0.4.0; 0.4.0 added
|
|
4
|
+
keywords and seconds, not features).
|
|
5
|
+
"Yes" = usable from Ruby, "Partial" = usable with fixed settings or a subset,
|
|
6
|
+
"No" = not exposed, with the reason. The Ruby-like layer of the `asterism`
|
|
7
|
+
gem builds on the same methods. The mruby gem (picoruby-asterism-zenoh) has
|
|
8
|
+
the 0.2.0 part of the API; its column says whether zenoh-pico could offer
|
|
9
|
+
the feature. Everything added in 0.3.0 is CRuby only for now.
|
|
10
|
+
|
|
11
|
+
| Area | zenoh-c | asterism-zenoh (CRuby) | Ruby | Possible on the boards (zenoh-pico) |
|
|
12
|
+
|---|---|---|---|---|
|
|
13
|
+
| Session: client / peer / listen | `z_open` | Yes | `Session.open(loc, mode:, listen:)` | Yes (done) |
|
|
14
|
+
| Session configuration | `zc_config_insert_json5`, `zc_config_from_str`, `zc_config_from_file` | Yes (0.3.0) | `config: {"key/path" => value}` (Ruby values sent as JSON), `config: "<JSON5>"`, `config_file:`. TLS, QUIC, WebSocket, authentication and timeouts are reachable this way; TLS is tested between two sessions | Partial (zenoh-pico has its own, smaller set of keys; no TLS on ESP32) |
|
|
15
|
+
| Scouting (finding peers / routers) | `z_scout`, multicast scouting, gossip | Yes (0.3.0) | `Asterism::Zenoh.scout(what:, timeout:)` -> `Hello` (zid, whatami, locators); `Session.open(scouting: true)` | Yes (off on purpose: the locator is given) |
|
|
16
|
+
| Session information | `z_info_zid`, `z_info_peers_zid`, `z_info_routers_zid`, `z_info_transports`, `z_info_links` | Yes (0.3.0) | `zid`, `connection_count` (count; `peers` is its deprecated name), `peer_zids`, `router_zids`, `transports`, `links` | Yes (zid lists); transports / links: No |
|
|
17
|
+
| put | `z_put` | Yes (0.3.0) | `put(key, payload, attachment:, encoding:, priority:, congestion_control:, express:, reliability:, timestamp:, allowed_destination:)` | Partial (encoding, priority, congestion control, express: yes) |
|
|
18
|
+
| delete | `z_delete` | Yes (0.3.0) | `session.delete(key, ...)` | Yes |
|
|
19
|
+
| Declared publisher | `z_declare_publisher`, `z_publisher_put`, `z_publisher_delete` | Yes (0.3.0) | `session.publisher(key, ...)` -> `put`, `delete`, `close` | Yes |
|
|
20
|
+
| Matching status (is anyone listening?) | `z_*_get_matching_status`, `z_*_declare_matching_listener` | Yes (0.3.0) | `matching?`, `matching_listener` -> `MatchingListener#each_pending` (polled) | Partial (zenoh-pico has it behind a build option) |
|
|
21
|
+
| Subscriber | `z_declare_subscriber` + FIFO channel | Yes | `each_pending { \|k, v, a\| }` as before; `each_sample` -> `Sample` (kind, encoding, timestamp, priority, congestion_control, express, reliability, source_zid) | Yes (done; the fields: Partial) |
|
|
22
|
+
| get | `z_get` | Yes | timeout, parameters, payload, attachment, target, consolidation; 0.3.0: encoding, priority, congestion control, express, accept_replies; `each_result` -> `Reply` with error replies; 0.4.0: `depth:` (the reply queue, 1024 by default) | Yes (done) |
|
|
23
|
+
| Declared querier | `z_declare_querier`, `z_querier_get` | Yes (0.3.0) | `session.querier(key, target:, consolidation:, timeout_ms:, ...)` -> `get(params, payload, attachment:, encoding:)`, `matching?`, `matching_listener` | Yes |
|
|
24
|
+
| Queryable | `z_declare_queryable`, `z_query_reply`, `z_query_reply_err`, `z_query_reply_del` | Yes (0.3.0) | `reply(..., encoding:, timestamp:, priority:, congestion_control:, express:)`, `reply_err(payload, encoding:)`, `reply_del(key)`, `query.encoding` | Yes |
|
|
25
|
+
| Liveliness | tokens, subscriber, get | Yes | `liveliness`, `liveliness_watch`, `liveliness_get`; `depth:` on the watch and the get (1024 by default on CRuby: a router sends every live token at once) | Yes (done) |
|
|
26
|
+
| Encoding (content types) | `z_encoding_from_str`, `z_encoding_to_string` | Yes (0.3.0) | Strings ("application/json", "text/plain;charset=utf-8"); default "zenoh/bytes" | Yes |
|
|
27
|
+
| Timestamps (HLC) | `z_timestamp_new`, `z_sample_timestamp`, timestamping | Yes (0.3.0) | `session.new_timestamp`, `Timestamp` (`ntp64`, `id`, `to_time`, Comparable), `put(timestamp: true)`, `Session.open(timestamping: true)` | Partial |
|
|
28
|
+
| Key expression operations | `z_keyexpr_intersects`, `includes`, `relation_to`, `join`, `concat`, `canonize`, `z_declare_keyexpr` | Yes (0.3.0) | `KeyExpr.new(str, autocanonize:)`, `intersects?`, `includes?`, `relation_to`, `join`, `concat`, `KeyExpr.canonize`, `KeyExpr.valid?`; `session.declare_keyexpr(key)`. A KeyExpr goes wherever a key String does | Yes |
|
|
29
|
+
| Advanced publisher / subscriber | `ze_declare_advanced_publisher`, `ze_declare_advanced_subscriber`, publisher detection, sample miss listener | Yes (0.3.0) | `advanced_publisher(key, cache:, sample_miss_detection:, publisher_detection:)`, `advanced_subscriber(key, history:, recovery:, subscriber_detection:)`, `detect_publishers`, `miss_listener`. A late rmw_zenoh subscriber with transient-local durability got the cached values | Partial (RAM to be measured) |
|
|
30
|
+
| Transport / link events | `z_declare_transport_events_listener`, `z_declare_link_events_listener` | Yes (0.3.0) | `session.transport_events(history:)`, `link_events` -> `EventListener#each_pending` (polled) of `TransportEvent` / `LinkEvent` | No |
|
|
31
|
+
| Logging | `zc_init_log_from_env_or`, `zc_try_init_log_from_env` | Yes (0.3.0) | `Asterism::Zenoh.init_log(level)`; written to standard output by zenoh-c; `RUST_LOG` wins | No |
|
|
32
|
+
| Error text | `zc_get_last_error` | Yes (0.3.0) | appended to the `Asterism::Zenoh::Error` messages of failed opens and declarations | No |
|
|
33
|
+
| Serialization helpers | `ze_serialize_*`, `ze_deserialize_*` | No | Not needed: MessagePack (objects) and CDR (ROS 2) are used; payloads stay Strings | Partial |
|
|
34
|
+
| Publication cache / querying subscriber | `ze_declare_publication_cache`, `ze_declare_querying_subscriber` | No | The older form of the advanced publisher / subscriber, which covers it | Partial |
|
|
35
|
+
| Shared memory | `z_shm_*` | No | Same-host only, and a Ruby String is copied anyway, so it would gain nothing | No |
|
|
36
|
+
| Background declarations | `z_declare_background_*` | No | Not needed: every object is closed by `close`, by its session or when it is freed | Partial |
|
|
37
|
+
| Cancellation of gets, source info on puts | `cancellation_token`, `source_info` | No | Rarely needed; a get ends by its time limit | No |
|
|
38
|
+
|
|
39
|
+
How receiving works, for all of the above: zenoh-c's callbacks copy what
|
|
40
|
+
arrives into a queue of plain C memory (no Ruby, no GVL), dropping the
|
|
41
|
+
oldest entry when it is full; Ruby takes the entries out by polling
|
|
42
|
+
(`each_pending`, `each_sample`, `each_result`). This holds for the matching
|
|
43
|
+
listeners, the transport / link events and the missed samples too.
|
|
44
|
+
|
|
45
|
+
Notes:
|
|
46
|
+
|
|
47
|
+
- The prebuilt zenoh-c that `gem install` downloads contains all of the above
|
|
48
|
+
(the "unstable" API included).
|
|
49
|
+
- An advanced publisher with a cache and without `sample_miss_detection`
|
|
50
|
+
needs a session with timestamping (`Session.open(..., timestamping: true)`).
|
|
51
|
+
- For ROS 2 transient-local, publish on the rmw_zenoh topic key with an
|
|
52
|
+
advanced publisher (`cache:`, `publisher_detection: true`,
|
|
53
|
+
`sample_miss_detection: true`) and declare the liveliness token with
|
|
54
|
+
durability transient local in its QoS (`":1:,10:,:,:,,"`).
|
|
55
|
+
`Asterism::ROS` does not do this by itself yet.
|