rsmp 0.48.4 → 0.51.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 +6 -0
- data/Gemfile.lock +19 -20
- data/README.md +27 -3
- data/Rakefile +3 -1
- data/documentation/cli.md +79 -4
- data/documentation/collecting_message.md +90 -170
- data/documentation/message_distribution.md +17 -3
- data/documentation/tasks.md +65 -117
- data/lib/rsmp/cli.rb +93 -5
- data/lib/rsmp/collect/collector.rb +117 -134
- data/lib/rsmp/collect/distributor.rb +16 -3
- data/lib/rsmp/collect/queue.rb +14 -6
- data/lib/rsmp/collect/receiver.rb +1 -1
- data/lib/rsmp/collect/state_collector.rb +11 -2
- data/lib/rsmp/collection.rb +12 -0
- data/lib/rsmp/completion.rb +49 -0
- data/lib/rsmp/component/component_base.rb +3 -3
- data/lib/rsmp/convert/export/json_schema/index.rb +8 -3
- data/lib/rsmp/convert/import/yaml.rb +12 -8
- data/lib/rsmp/delivery.rb +18 -0
- data/lib/rsmp/event.rb +19 -0
- data/lib/rsmp/event_source.rb +36 -0
- data/lib/rsmp/exchange.rb +10 -0
- data/lib/rsmp/failure.rb +23 -0
- data/lib/rsmp/helpers/error.rb +14 -23
- data/lib/rsmp/message.rb +9 -7
- data/lib/rsmp/node/node.rb +3 -21
- data/lib/rsmp/node/site/connections.rb +4 -9
- data/lib/rsmp/node/site/site.rb +31 -16
- data/lib/rsmp/node/supervisor/modules/connection.rb +19 -5
- data/lib/rsmp/node/supervisor/modules/sites.rb +16 -5
- data/lib/rsmp/node/supervisor/supervisor.rb +13 -14
- data/lib/rsmp/node/task.rb +59 -53
- data/lib/rsmp/proxy/modules/acknowledgements.rb +22 -9
- data/lib/rsmp/proxy/modules/lifecycle.rb +134 -0
- data/lib/rsmp/proxy/modules/receive.rb +109 -66
- data/lib/rsmp/proxy/modules/send.rb +109 -51
- data/lib/rsmp/proxy/modules/state.rb +15 -5
- data/lib/rsmp/proxy/modules/tasks.rb +96 -70
- data/lib/rsmp/proxy/modules/versions.rb +8 -8
- data/lib/rsmp/proxy/modules/watchdogs.rb +15 -2
- data/lib/rsmp/proxy/proxy.rb +4 -66
- data/lib/rsmp/proxy/site/connection.rb +27 -22
- data/lib/rsmp/proxy/site/modules/aggregated_status.rb +18 -8
- data/lib/rsmp/proxy/site/modules/alarms.rb +45 -38
- data/lib/rsmp/proxy/site/modules/commands.rb +18 -8
- data/lib/rsmp/proxy/site/modules/status.rb +44 -19
- data/lib/rsmp/proxy/site/site_proxy.rb +12 -10
- data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/alarms.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/commands.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/message_buffer.rb +8 -9
- data/lib/rsmp/proxy/supervisor/modules/status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/status_updates.rb +1 -1
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +34 -30
- data/lib/rsmp/result.rb +95 -0
- data/lib/rsmp/schema/validation.rb +5 -3
- data/lib/rsmp/sxl/interface.rb +8 -1
- data/lib/rsmp/sxl/processing/catalogue.rb +61 -0
- data/lib/rsmp/sxl/processing/conflict_checker.rb +30 -0
- data/lib/rsmp/sxl/processing/dependency_graph.rb +37 -0
- data/lib/rsmp/sxl/processing/document.rb +158 -0
- data/lib/rsmp/sxl/processing/error.rb +8 -0
- data/lib/rsmp/sxl/processing/manifest.rb +144 -0
- data/lib/rsmp/sxl/processing/natural_sort.rb +22 -0
- data/lib/rsmp/sxl/processing/resolver.rb +94 -0
- data/lib/rsmp/sxl/processing/version_requirement.rb +86 -0
- data/lib/rsmp/sxl/processing.rb +18 -0
- data/lib/rsmp/sxl/supervisor_interface.rb +8 -1
- data/lib/rsmp/termination.rb +10 -0
- data/lib/rsmp/tlc/modules/plans.rb +0 -2
- data/lib/rsmp/tlc/proxy/control.rb +50 -16
- data/lib/rsmp/tlc/proxy/detectors.rb +15 -4
- data/lib/rsmp/tlc/proxy/io.rb +46 -31
- data/lib/rsmp/tlc/proxy/plans.rb +36 -19
- data/lib/rsmp/tlc/proxy/plans_bang.rb +44 -0
- data/lib/rsmp/tlc/proxy/status.rb +47 -23
- data/lib/rsmp/tlc/proxy/system.rb +16 -4
- data/lib/rsmp/tlc/supervisor_interface.rb +7 -1
- data/lib/rsmp/tlc/traffic_controller_site.rb +6 -9
- data/lib/rsmp/validation.rb +22 -0
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +19 -4
- data/rsmp.gemspec +1 -1
- metadata +25 -4
- data/lib/rsmp/collect/collector/status.rb +0 -34
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 363ee5c8eb68f060a0f464939cc6a4444265568821fab38186992b189de29dd0
|
|
4
|
+
data.tar.gz: f53b952f5877fd3c17b8d90ee82b7e1412432ad34a68cdcac3736320674cce8f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed82fd73d09b5bcbd83c5c675a9e8ff9ed8d925e23f7cd059dd8efd9ca413e672f53ca0f3f76717114686d541c88b2a84f6dcd204a57338688b4c5db11dec1c9
|
|
7
|
+
data.tar.gz: 2598d67cfd2d5a66bf466b6c8901d2013689160b18cf4fead369111c41dd0cc2ee760874551fc0b86d70f284b21a38b68aa5810c534a0a3563b79ef028f972a9
|
data/CHANGELOG.md
CHANGED
|
@@ -705,3 +705,9 @@ Initial release.
|
|
|
705
705
|
|
|
706
706
|
## 0.48.2
|
|
707
707
|
- fix legacy TLC M0001 schemas to use scalar command arguments
|
|
708
|
+
|
|
709
|
+
## 0.50.0
|
|
710
|
+
- return immutable results for finite sends, waits, validation, and collection
|
|
711
|
+
- publish typed events for expected peer and connection failures
|
|
712
|
+
- preserve unexpected exceptions and backtraces through structured Async task trees
|
|
713
|
+
- add explicit bang variants for callers that prefer exception-based handling
|
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
rsmp (0.
|
|
5
|
-
async (~> 2.
|
|
4
|
+
rsmp (0.51.0)
|
|
5
|
+
async (~> 2.45)
|
|
6
6
|
colorize (~> 1.1)
|
|
7
7
|
io-endpoint (~> 0.17)
|
|
8
8
|
io-stream (~> 0.13)
|
|
@@ -15,15 +15,13 @@ GEM
|
|
|
15
15
|
remote: https://rubygems.org/
|
|
16
16
|
specs:
|
|
17
17
|
ast (2.4.3)
|
|
18
|
-
async (2.
|
|
18
|
+
async (2.45.1)
|
|
19
19
|
console (~> 1.29)
|
|
20
20
|
fiber-annotation
|
|
21
|
-
io-event (~> 1.
|
|
22
|
-
metrics (~> 0.12)
|
|
23
|
-
traces (~> 0.18)
|
|
21
|
+
io-event (~> 1.21)
|
|
24
22
|
bigdecimal (4.1.2)
|
|
25
23
|
colorize (1.1.0)
|
|
26
|
-
console (1.
|
|
24
|
+
console (1.37.0)
|
|
27
25
|
fiber-annotation
|
|
28
26
|
fiber-local (~> 1.1)
|
|
29
27
|
json
|
|
@@ -32,22 +30,24 @@ GEM
|
|
|
32
30
|
fiber-storage
|
|
33
31
|
fiber-storage (1.0.1)
|
|
34
32
|
hana (1.3.7)
|
|
35
|
-
io-endpoint (0.
|
|
36
|
-
|
|
37
|
-
io-
|
|
38
|
-
|
|
33
|
+
io-endpoint (0.18.0)
|
|
34
|
+
openssl (>= 3.3.0)
|
|
35
|
+
io-event (1.21.1)
|
|
36
|
+
io-stream (0.14.0)
|
|
37
|
+
openssl (>= 3.3)
|
|
38
|
+
json (2.21.2)
|
|
39
39
|
json_schemer (2.5.0)
|
|
40
40
|
bigdecimal
|
|
41
41
|
hana (~> 1.3)
|
|
42
42
|
regexp_parser (~> 2.0)
|
|
43
43
|
simpleidn (~> 0.2)
|
|
44
|
-
language_server-protocol (3.17.0.
|
|
44
|
+
language_server-protocol (3.17.0.6)
|
|
45
45
|
lint_roller (1.1.0)
|
|
46
46
|
logger (1.7.0)
|
|
47
|
-
|
|
47
|
+
openssl (4.0.2)
|
|
48
48
|
ostruct (0.6.3)
|
|
49
49
|
parallel (2.1.0)
|
|
50
|
-
parser (3.3.
|
|
50
|
+
parser (3.3.12.0)
|
|
51
51
|
ast (~> 2.4.1)
|
|
52
52
|
racc
|
|
53
53
|
prism (1.9.0)
|
|
@@ -55,8 +55,8 @@ GEM
|
|
|
55
55
|
rainbow (3.1.1)
|
|
56
56
|
rake (13.4.2)
|
|
57
57
|
regexp_parser (2.12.0)
|
|
58
|
-
rubocop (1.
|
|
59
|
-
json (
|
|
58
|
+
rubocop (1.90.0)
|
|
59
|
+
json (>= 2.3)
|
|
60
60
|
language_server-protocol (~> 3.17.0.2)
|
|
61
61
|
lint_roller (~> 1.1.0)
|
|
62
62
|
parallel (>= 1.10)
|
|
@@ -66,21 +66,20 @@ GEM
|
|
|
66
66
|
rubocop-ast (>= 1.49.0, < 2.0)
|
|
67
67
|
ruby-progressbar (~> 1.7)
|
|
68
68
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
69
|
-
rubocop-ast (1.
|
|
69
|
+
rubocop-ast (1.50.0)
|
|
70
70
|
parser (>= 3.3.7.2)
|
|
71
71
|
prism (~> 1.7)
|
|
72
72
|
rubocop-rake (0.7.1)
|
|
73
73
|
lint_roller (~> 1.1)
|
|
74
74
|
rubocop (>= 1.72.1)
|
|
75
75
|
ruby-progressbar (1.13.0)
|
|
76
|
-
simpleidn (0.
|
|
77
|
-
sus (0.37.
|
|
76
|
+
simpleidn (0.3.0)
|
|
77
|
+
sus (0.37.2)
|
|
78
78
|
sus-fixtures-async (0.2.0)
|
|
79
79
|
async
|
|
80
80
|
sus (~> 0.10)
|
|
81
81
|
thor (1.5.0)
|
|
82
82
|
timecop (0.9.11)
|
|
83
|
-
traces (0.18.2)
|
|
84
83
|
unicode-display_width (3.2.0)
|
|
85
84
|
unicode-emoji (~> 4.1)
|
|
86
85
|
unicode-emoji (4.2.0)
|
data/README.md
CHANGED
|
@@ -20,12 +20,20 @@ The RSMP::Site and RSMP::Supervisor classes can be used to run a RSMP site.
|
|
|
20
20
|
|
|
21
21
|
```ruby
|
|
22
22
|
require 'rsmp'
|
|
23
|
-
|
|
23
|
+
Async do |task|
|
|
24
|
+
site = RSMP::Site.new
|
|
25
|
+
site.start(parent: task)
|
|
26
|
+
site.wait
|
|
27
|
+
end
|
|
24
28
|
```
|
|
25
29
|
|
|
26
30
|
```ruby
|
|
27
31
|
require 'rsmp'
|
|
28
|
-
|
|
32
|
+
Async do |task|
|
|
33
|
+
supervisor = RSMP::Supervisor.new
|
|
34
|
+
supervisor.start(parent: task)
|
|
35
|
+
supervisor.wait
|
|
36
|
+
end
|
|
29
37
|
```
|
|
30
38
|
|
|
31
39
|
By default, a site will try to connect to a single supervisor on localhost 127.0.0.1, port 12111. By default, a supervisor will listen for sites on port 12111 and accept any site.
|
|
@@ -131,12 +139,19 @@ the RSMP Core and SXL source repositories.
|
|
|
131
139
|
Core and SXL schemas are selected with a flat map:
|
|
132
140
|
|
|
133
141
|
```ruby
|
|
134
|
-
RSMP::Schema.validate(message, {
|
|
142
|
+
validation = RSMP::Schema.validate(message, {
|
|
135
143
|
core: '3.3.0',
|
|
136
144
|
tlc: '1.3.0'
|
|
137
145
|
})
|
|
146
|
+
warn validation.message if validation.invalid?
|
|
138
147
|
```
|
|
139
148
|
|
|
149
|
+
Finite operations such as waits, sends, and response collection return
|
|
150
|
+
`RSMP::Result`. Expected peer, transport, and timeout failures are values;
|
|
151
|
+
unexpected implementation errors still propagate with their original
|
|
152
|
+
backtraces. See [finite operations and collection](documentation/collecting_message.md)
|
|
153
|
+
and [Async task ownership](documentation/tasks.md).
|
|
154
|
+
|
|
140
155
|
Sites and supervisors configure one or more SXLs with `sxls`:
|
|
141
156
|
|
|
142
157
|
```yaml
|
|
@@ -149,6 +164,15 @@ See [configuration](documentation/configuration.md) for connection-role examples
|
|
|
149
164
|
## Command-line tool
|
|
150
165
|
Tools for easily running RSMP supervisors and sites. The binary is called ```rsmp```.
|
|
151
166
|
|
|
167
|
+
It also provides SXL dependency and manifest tooling:
|
|
168
|
+
|
|
169
|
+
```console
|
|
170
|
+
$ rsmp sxl resolve path/to/sxl.yaml --source path/to/sxl-catalog
|
|
171
|
+
$ rsmp sxl verify manifest.yaml --source path/to/sxl-catalog
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
See [the CLI reference](documentation/cli.md#rsmp-sxl-resolve) for source layout, requirement syntax, and options.
|
|
175
|
+
|
|
152
176
|
The ```supervisor``` command will start an RSMP supervisor, which sites can connect to:
|
|
153
177
|
|
|
154
178
|
```console
|
data/Rakefile
CHANGED
|
@@ -23,7 +23,9 @@ end
|
|
|
23
23
|
def require_minimum_core_version!(source_path)
|
|
24
24
|
yaml = YAML.load_file(source_path)
|
|
25
25
|
minimum_core_version = yaml.dig('meta', 'minimum_core_version')
|
|
26
|
-
|
|
26
|
+
return unless minimum_core_version.nil? || minimum_core_version.to_s.empty?
|
|
27
|
+
|
|
28
|
+
raise "Missing meta.minimum_core_version in #{source_path}"
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
# Update vendored schemas from source repos.
|
data/documentation/cli.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# RSMP CLI
|
|
2
2
|
|
|
3
|
-
The `rsmp` command can run a site, run a supervisor, validate config files,
|
|
3
|
+
The `rsmp` command can run a site, run a supervisor, validate config files, resolve SXL dependencies,
|
|
4
|
+
verify SXL manifests, generate JSON Schemas from an SXL, and print the gem version.
|
|
4
5
|
|
|
5
6
|
Use help at any level:
|
|
6
7
|
|
|
@@ -8,8 +9,10 @@ Use help at any level:
|
|
|
8
9
|
$ rsmp help
|
|
9
10
|
$ rsmp help site
|
|
10
11
|
$ rsmp help supervisor
|
|
11
|
-
$ rsmp help
|
|
12
|
-
$ rsmp help
|
|
12
|
+
$ rsmp config help check
|
|
13
|
+
$ rsmp schema help generate
|
|
14
|
+
$ rsmp sxl help resolve
|
|
15
|
+
$ rsmp sxl help verify
|
|
13
16
|
```
|
|
14
17
|
|
|
15
18
|
## Quick Examples
|
|
@@ -66,6 +69,18 @@ Generate JSON Schema files from an SXL YAML file:
|
|
|
66
69
|
$ rsmp schema generate --in schemas/tlc/1.3.0/sxl.yaml --out /tmp/tlc-schema
|
|
67
70
|
```
|
|
68
71
|
|
|
72
|
+
Resolve an SXL and its dependencies from local sources:
|
|
73
|
+
|
|
74
|
+
```console
|
|
75
|
+
$ rsmp sxl resolve schema/sxl.yaml --source ../sxl-catalog
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Verify the resulting manifest against the same sources:
|
|
79
|
+
|
|
80
|
+
```console
|
|
81
|
+
$ rsmp sxl verify manifest.yaml --source ../sxl-catalog
|
|
82
|
+
```
|
|
83
|
+
|
|
69
84
|
## Configuration Files
|
|
70
85
|
|
|
71
86
|
Most real runs should use YAML config files. The CLI accepts `--config`, `-c`, and `--options` as aliases:
|
|
@@ -206,10 +221,70 @@ Options:
|
|
|
206
221
|
- `--in PATH`, `-i PATH`: path to the input `sxl.yaml`. Defaults to `sxl.yaml`.
|
|
207
222
|
- `--out PATH`, `-o PATH`: output directory. Defaults to the current directory.
|
|
208
223
|
|
|
209
|
-
The
|
|
224
|
+
The input can use either the top-level `components` or `objects` mapping. Both forms are normalized to
|
|
225
|
+
`components` before processing. The command
|
|
226
|
+
writes the generated status, command, alarm, root schema, definitions, and `sxl_index.json` files to the output directory.
|
|
210
227
|
|
|
211
228
|
If the input file is missing, the command prints an error and exits with status `1`.
|
|
212
229
|
|
|
230
|
+
### `rsmp sxl resolve`
|
|
231
|
+
|
|
232
|
+
Resolves direct and transitive SXL dependencies, checks for conflicting component types and message codes,
|
|
233
|
+
and writes a manifest:
|
|
234
|
+
|
|
235
|
+
```console
|
|
236
|
+
$ rsmp sxl resolve schema/sxl.yaml --source ../sxl-catalog
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Roots can be SXL YAML paths or `name:requirement` specifications. Several roots can be supplied
|
|
240
|
+
for a site SXL list:
|
|
241
|
+
|
|
242
|
+
```console
|
|
243
|
+
$ rsmp sxl resolve traffic_light_controller:~1.3 public_priority:2.1.0 \
|
|
244
|
+
--source ../sxl-catalog --out site-manifest.yaml
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Quote a requirement when it contains spaces:
|
|
248
|
+
|
|
249
|
+
```console
|
|
250
|
+
$ rsmp sxl resolve 'traffic_light_controller:>=1.3.0 and <2.0.0' --source ../sxl-catalog
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Source arguments can be individual YAML files or directories. Directories are searched recursively for files
|
|
254
|
+
named `sxl.yaml` or `sxl.yml`. Every source must use top-level `meta`, optional `prefix`, optional
|
|
255
|
+
`dependencies`, and either `components` or `objects`. Both input forms are normalized to `components`.
|
|
256
|
+
Sources are indexed by exact `meta.name` and `meta.version`.
|
|
257
|
+
|
|
258
|
+
Options:
|
|
259
|
+
|
|
260
|
+
- `--source PATH...`, `-s PATH...`: one or more local source files or directories.
|
|
261
|
+
- `--out PATH`, `-o PATH`: manifest output path. Defaults to `manifest.yaml`.
|
|
262
|
+
- `--format VERSION`: RSMP Core version written as `meta.format`. Defaults to the latest Core version bundled with the gem.
|
|
263
|
+
- `--force`, `-f`: replace an existing output file. Without this option, existing files are preserved and the command fails.
|
|
264
|
+
|
|
265
|
+
The resolver supports exact versions, `>`, `>=`, `<`, `<=`, `~`, and two comparisons joined by `and`. It tries
|
|
266
|
+
available versions newest first, rejects unsatisfied requirements and dependency cycles, applies each SXL prefix,
|
|
267
|
+
and rejects component types or message codes defined by more than one resolved SXL. For non-zero major versions,
|
|
268
|
+
`~1.3` means `>=1.3.0 and <2.0.0`. A major-zero compatibility requirement must include all three parts and is exact,
|
|
269
|
+
for example `~0.3.1` matches only `0.3.1`.
|
|
270
|
+
|
|
271
|
+
### `rsmp sxl verify`
|
|
272
|
+
|
|
273
|
+
Verifies a manifest against locally supplied SXL sources:
|
|
274
|
+
|
|
275
|
+
```console
|
|
276
|
+
$ rsmp sxl verify manifest.yaml --source ../sxl-catalog
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
The manifest defaults to `manifest.yaml`. `--source PATH...` has the same meaning as for `sxl resolve`.
|
|
280
|
+
Verification checks the manifest shape and metadata, natural name order, availability of every exact SXL version,
|
|
281
|
+
the complete dependency closure and version requirements, dependency cycles, Core-format compatibility, and component
|
|
282
|
+
or message-code conflicts. A valid manifest prints `OK`; a failure prints an error and exits with status `1`.
|
|
283
|
+
|
|
284
|
+
Because the manifest format records only exact resolved versions and does not distinguish roots from dependencies,
|
|
285
|
+
verification checks validity but cannot determine whether the manifest still selects the newest versions in a changed
|
|
286
|
+
source catalogue. Run `sxl resolve` again to refresh version selection.
|
|
287
|
+
|
|
213
288
|
### `rsmp version`
|
|
214
289
|
|
|
215
290
|
Prints the installed `rsmp` gem version:
|
|
@@ -1,226 +1,146 @@
|
|
|
1
|
-
#
|
|
2
|
-
You often need to collect messages or responses. The collector classes are used to collect message asyncronously. Other tasks continue until the collection completes, time outs or is cancelled.
|
|
1
|
+
# Finite operations and message collection
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Messages that match the relevant criteria are stored by the collector.
|
|
9
|
-
|
|
10
|
-
When the collection is done, the collector detaches from the distributor, and returns the status.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
## Collector
|
|
14
|
-
Class used for collecting messages filtered by message type, direction and/or component id. A block can be used for custom filtering.
|
|
15
|
-
|
|
16
|
-
You can choose to collect a specific number of message and/or for a specific duration.
|
|
17
|
-
|
|
18
|
-
A collector has a status, which is `:ready` initialialy. When you start collecting, it changes to `:collecting`. It will be `:ok` once collection completes successfully, or `:cancel` if it was cancelled to to some error or by a filter block.
|
|
19
|
-
|
|
20
|
-
### Initialization
|
|
21
|
-
When you create a collector, you provide a Filter to specify the messages types you want to collect. You can also specify ingoing and/or outgoing direction and the RSMP component.
|
|
3
|
+
RSMP distinguishes expected operational failures from implementation defects.
|
|
4
|
+
Operations that can normally time out, be rejected, or lose their connection
|
|
5
|
+
return an `RSMP::Result`. They do not use exceptions for ordinary control flow.
|
|
22
6
|
|
|
23
7
|
```ruby
|
|
24
|
-
|
|
25
|
-
num: 10,
|
|
26
|
-
filter: Filter.new(ingoing: true, outgoing: true)
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
num: The number of messages to collect. If not provided, a timeout must be set instead.
|
|
30
|
-
filter: filter to identify the types of messages to look for.
|
|
8
|
+
result = proxy.wait_for_state(:ready, timeout: 5)
|
|
31
9
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
type: 'Alarm',
|
|
38
|
-
ingoing: true,
|
|
39
|
-
outgoing: false,
|
|
40
|
-
component: 'DL1'
|
|
41
|
-
)
|
|
10
|
+
if result.success?
|
|
11
|
+
puts "Reached #{result.value}"
|
|
12
|
+
else
|
|
13
|
+
warn "#{result.failure.code}: #{result.failure.message}"
|
|
14
|
+
end
|
|
42
15
|
```
|
|
43
16
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
outgoing: Whether to collect outgoing messages. Defaults to true
|
|
47
|
-
component: An RSMP component id.
|
|
17
|
+
`RSMP::Result::Success` contains `value`. `RSMP::Result::Failure` contains an
|
|
18
|
+
immutable `RSMP::Failure` with:
|
|
48
19
|
|
|
49
|
-
|
|
50
|
-
|
|
20
|
+
- `code`: a stable symbol such as `:timeout`, `:not_ready`,
|
|
21
|
+
`:message_rejected`, or `:disconnected`.
|
|
22
|
+
- `message`: a human-readable explanation.
|
|
23
|
+
- `source`: where the failure originated, such as `:peer`, `:transport`,
|
|
24
|
+
`:connection`, `:timeout`, or `:local`.
|
|
25
|
+
- `context`: structured details such as the message and connection session.
|
|
26
|
+
- `cause`: the underlying exception, when an expected low-level exception was
|
|
27
|
+
translated at a boundary.
|
|
28
|
+
|
|
29
|
+
Use the corresponding bang method when exception-based handling is more
|
|
30
|
+
convenient. A failed result is then raised as `RSMP::OperationError`; its
|
|
31
|
+
`failure` attribute retains the structured failure.
|
|
51
32
|
|
|
52
33
|
```ruby
|
|
53
|
-
|
|
54
|
-
collector.messages # => collected messages
|
|
34
|
+
proxy.wait_for_state!(:ready, timeout: 5)
|
|
55
35
|
```
|
|
56
36
|
|
|
57
|
-
|
|
37
|
+
Unexpected exceptions are never converted into a `Result`. They propagate with
|
|
38
|
+
their original class and backtrace because they indicate a bug in the library or
|
|
39
|
+
calling application.
|
|
58
40
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```
|
|
41
|
+
Message handlers can reject schema-valid but semantically invalid peer input by
|
|
42
|
+
raising `RSMP::PeerMessageError` or one of its domain subclasses, normally
|
|
43
|
+
`RSMP::MessageRejected`. The receive boundary converts only this explicit error
|
|
44
|
+
family to a peer failure. It does not rescue arbitrary `StandardError` values.
|
|
64
45
|
|
|
65
|
-
|
|
66
|
-
You can use a block to do extra filtering. The block will be callled for each messages that passes the Filter provided when initializing the collector.
|
|
46
|
+
## Validation
|
|
67
47
|
|
|
68
|
-
|
|
48
|
+
Schema mismatches are expected when communicating with an external peer.
|
|
49
|
+
`RSMP::Schema.validate` and `message.validate` return an `RSMP::Validation`:
|
|
69
50
|
|
|
70
51
|
```ruby
|
|
71
|
-
|
|
72
|
-
|
|
52
|
+
validation = message.validate(core: '3.3.0', tlc: '1.3.0')
|
|
53
|
+
|
|
54
|
+
unless validation.valid?
|
|
55
|
+
warn validation.message
|
|
56
|
+
validation.violations.each { |violation| warn violation.inspect }
|
|
73
57
|
end
|
|
74
58
|
```
|
|
75
59
|
|
|
76
|
-
|
|
77
|
-
|
|
60
|
+
`message.validate!` is the explicit raising variant. Invalid API arguments,
|
|
61
|
+
missing schema configuration, and unknown schema versions still raise directly
|
|
62
|
+
because they are programming or configuration errors rather than invalid peer
|
|
63
|
+
messages.
|
|
78
64
|
|
|
79
|
-
|
|
65
|
+
## Collectors
|
|
80
66
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
The method collect!() will raise exceptions in case of errors, and will return the collect message directly.
|
|
67
|
+
A collector attaches to a message distributor, such as `SiteProxy` or
|
|
68
|
+
`SupervisorProxy`, and waits for matching messages. Give it a count, a timeout,
|
|
69
|
+
or a block that completes or cancels the collection.
|
|
85
70
|
|
|
86
71
|
```ruby
|
|
87
|
-
|
|
72
|
+
filter = RSMP::Filter.new(type: 'Alarm', ingoing: true, component: 'DL1')
|
|
73
|
+
collector = RSMP::Collector.new(proxy, filter: filter, num: 2, timeout: 5)
|
|
74
|
+
result = collector.collect
|
|
88
75
|
```
|
|
89
76
|
|
|
90
|
-
|
|
77
|
+
A successful collector returns `Result<RSMP::Collection>`. A collection is an
|
|
78
|
+
immutable snapshot containing `messages` and, for state collectors, `reached`
|
|
79
|
+
and `matcher_status`.
|
|
91
80
|
|
|
92
81
|
```ruby
|
|
93
|
-
|
|
82
|
+
if result.success?
|
|
83
|
+
result.value.messages.each { |message| puts message }
|
|
84
|
+
end
|
|
94
85
|
```
|
|
95
86
|
|
|
87
|
+
`collect!` and `wait!` return the message array directly and raise
|
|
88
|
+
`RSMP::OperationError` for an expected failure.
|
|
96
89
|
|
|
97
|
-
|
|
98
|
-
The collector can optionally cancel collection in special cases, controlled by the `:cancel` option provided when contructing the collector.
|
|
90
|
+
To start without waiting:
|
|
99
91
|
|
|
100
92
|
```ruby
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
schema_error: true
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
result = collector.collect options
|
|
93
|
+
collector.start
|
|
94
|
+
# Perform another operation.
|
|
95
|
+
result = collector.wait
|
|
108
96
|
```
|
|
109
97
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
A typical scenaria is that you send a command or status request, and want to collect the response. But if the original message is rejected by the site, you will received a NotAck instead of a reply. The collector classes can handle this, as long as you provide the message id of the original request in the `m_id` key of teh options when you construct the collector.
|
|
116
|
-
|
|
117
|
-
If a NotAck is received with a matching `oMId` (original message id), the collection is cancelled.
|
|
98
|
+
Collectors start in an inactive state, become active after `start`, and detach
|
|
99
|
+
from the distributor exactly once when they succeed or fail. A matching
|
|
100
|
+
`MessageNotAck`, timeout, invalid peer message, connection end, or explicit
|
|
101
|
+
`cancel` resolves the collector with a failure. An exception raised by a custom
|
|
102
|
+
collector callback rejects its completion and propagates unchanged.
|
|
118
103
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
Note that a single RSMP status message can contain multiple status items. Unlike MessageCollector, a StatusCollector therefore operates on items, rather than messages, and you can't specify a number of messages to collect.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
### Criteria
|
|
126
|
-
You construct a StatusCollector with set of criteria, specifying the status codes, names, and optionally values that must be met.
|
|
127
|
-
|
|
128
|
-
### Collecting
|
|
129
|
-
When you start collection, it will complete once all criteria are all fulfilled, the timeout is reached or a custom filtering block aborts the collection.
|
|
104
|
+
Custom blocks return `:keep` to retain a matching message. They can also call
|
|
105
|
+
`collector.cancel(reason)` explicitly.
|
|
130
106
|
|
|
131
107
|
```ruby
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Custom filtering
|
|
137
|
-
You can use a block to do extra filtering. The block will be called for each individual status item that fulfils all criteria, like status code and name, component, etc.
|
|
138
|
-
|
|
139
|
-
Like with MessageCollector, the block must return a hash specifing whether to keep the message and whether to continue collection.
|
|
108
|
+
result = collector.collect do |message|
|
|
109
|
+
next :keep if useful?(message)
|
|
140
110
|
|
|
141
|
-
|
|
142
|
-
matcher = StatusCollector.new(options)
|
|
143
|
-
result = matcher.collect(options) do |message,item|
|
|
144
|
-
next(:keep) if good_item?(item) # keep item
|
|
111
|
+
collector.cancel('No longer needed') if finished?
|
|
145
112
|
end
|
|
146
113
|
```
|
|
147
114
|
|
|
148
|
-
## Sending
|
|
149
|
-
The method `send_command` sends a CommandRequest to the site and returns the sent message. `component:` defaults to `main.c_id`.
|
|
150
|
-
|
|
151
|
-
```ruby
|
|
152
|
-
message = send_command(
|
|
153
|
-
[{'cCI' => 'M0001', 'n' => 'status', 'v' => 'NormalControl'}],
|
|
154
|
-
component: 'AA+BBCCC=DDDEE001'
|
|
155
|
-
)
|
|
156
|
-
```
|
|
115
|
+
## Sending and collecting atomically
|
|
157
116
|
|
|
158
|
-
|
|
117
|
+
High-level send methods follow the same non-bang/bang convention:
|
|
159
118
|
|
|
160
119
|
```ruby
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
within: 5,
|
|
164
|
-
component: 'AA+BBCCC=DDDEE001'
|
|
165
|
-
)
|
|
166
|
-
collector.ok!
|
|
120
|
+
result = proxy.send_command(command_list, component: 'C1')
|
|
121
|
+
message = proxy.send_command!(command_list, component: 'C1')
|
|
167
122
|
```
|
|
168
123
|
|
|
169
|
-
|
|
170
|
-
|
|
124
|
+
Methods ending in `_and_collect` start their collector before sending, so an
|
|
125
|
+
immediate peer response cannot be missed. They return `Result<RSMP::Exchange>`.
|
|
126
|
+
The exchange contains the request and the immutable completed collection.
|
|
171
127
|
|
|
172
128
|
```ruby
|
|
173
|
-
result =
|
|
174
|
-
[{'sCI' => 'S0001', 'n' => 'signalgroupstatus'}],
|
|
175
|
-
component: 'AA+BBCCC=DDDEE001'
|
|
176
|
-
)
|
|
177
|
-
result[:sent] # => the StatusRequest message
|
|
178
|
-
```
|
|
129
|
+
result = proxy.send_command_and_collect(command_list, component: 'C1', within: 5)
|
|
179
130
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
[{'sCI' => 'S0001', 'n' => 'signalgroupstatus'}],
|
|
185
|
-
within: 5,
|
|
186
|
-
component: 'AA+BBCCC=DDDEE001'
|
|
187
|
-
)
|
|
188
|
-
collector.ok!
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
## Subscribing to status updates
|
|
192
|
-
The method `subscribe_to_status` sends a StatusSubscribe message to the site and returns `{ sent: message }`. `component:` defaults to `main.c_id`.
|
|
193
|
-
|
|
194
|
-
### Without collection
|
|
195
|
-
|
|
196
|
-
```ruby
|
|
197
|
-
result = subscribe_to_status(
|
|
198
|
-
[{'sCI' => 'S0001', 'n' => 'signalgroupstatus', 'uRt' => '1'}],
|
|
199
|
-
component: 'AA+BBCCC=DDDEE001'
|
|
200
|
-
)
|
|
201
|
-
result[:sent] # => the StatusSubscribe message
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
If you want to manually collect incoming status updates after subscribing, start a collector before subscribing so you don't miss early responses:
|
|
205
|
-
|
|
206
|
-
```ruby
|
|
207
|
-
task = async do
|
|
208
|
-
MessageCollector.new(options).collect(num: 5, timeout: 10)
|
|
131
|
+
if result.success?
|
|
132
|
+
exchange = result.value
|
|
133
|
+
puts exchange.request
|
|
134
|
+
puts exchange.collection.messages
|
|
209
135
|
end
|
|
210
|
-
subscribe_to_status(status_list)
|
|
211
|
-
task.wait
|
|
212
136
|
```
|
|
213
137
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
Use `subscribe_to_status_and_collect` to subscribe and collect status updates matching the criteria. It returns a collector; call `.ok!` to raise on NotAck or timeout.
|
|
138
|
+
Status requests and subscriptions use the same shape:
|
|
217
139
|
|
|
218
140
|
```ruby
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
within: 5,
|
|
222
|
-
component: 'AA+BBCCC=DDDEE001'
|
|
223
|
-
)
|
|
224
|
-
collector.ok!
|
|
141
|
+
result = proxy.request_status_and_collect(status_list, component: 'C1', within: 5)
|
|
142
|
+
result = proxy.subscribe_to_status_and_collect(subscription_list, component: 'C1', within: 5)
|
|
225
143
|
```
|
|
226
144
|
|
|
145
|
+
The raising variants are `send_command_and_collect!`,
|
|
146
|
+
`request_status_and_collect!`, and `subscribe_to_status_and_collect!`.
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
# Message distribution
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
```text
|
|
4
|
+
Proxy -- Distributor --> message receivers
|
|
5
|
+
|
|
|
6
|
+
+-- EventSource ----> explicit node event subscribers
|
|
7
|
+
```
|
|
4
8
|
|
|
5
|
-
A proxy distributes messages to receivers
|
|
9
|
+
A proxy distributes messages to receivers when they are installed. Distribution
|
|
10
|
+
uses a receiver snapshot, so a receiver may safely detach during delivery.
|
|
6
11
|
|
|
7
12
|
Collectors are special receivers that wait for specific messages, and are used to implement methods for waiting for RSMP responses, statuses, alarms, etc.
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
Archive is not a receiver. It stores messages and other log entries through the
|
|
15
|
+
logging interface.
|
|
10
16
|
|
|
11
17
|
## Distributor
|
|
12
18
|
A module that handles distributing messages to receivers.
|
|
@@ -14,9 +20,17 @@ A module that handles distributing messages to receivers.
|
|
|
14
20
|
## Receiver
|
|
15
21
|
Receives messages as long as it's installed into a distributor.
|
|
16
22
|
|
|
23
|
+
Collectors also receive relevant `RSMP::Event` values, such as
|
|
24
|
+
`:invalid_message` and `:connection_ended`. This lets expected peer and
|
|
25
|
+
transport failures resolve a pending collection without raising.
|
|
26
|
+
|
|
17
27
|
## Collector
|
|
18
28
|
Includes the Receiver module to wait for specific messages. Once received
|
|
19
29
|
the client receives the collection.
|
|
20
30
|
|
|
21
31
|
## Proxy
|
|
22
32
|
A proxy includes the Distributor module and distributes each message to receivers after processing it.
|
|
33
|
+
|
|
34
|
+
Long-running occurrences are additionally published to the owning node's
|
|
35
|
+
explicit event subscribers. Event delivery is synchronous and ordered; there is
|
|
36
|
+
no implicit global error queue.
|