rsmp 0.45.0 → 0.45.1

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rubocop.yaml +1 -1
  3. data/.github/workflows/sus.yaml +1 -1
  4. data/.gitignore +0 -1
  5. data/CHANGELOG.md +12 -1
  6. data/Gemfile +0 -2
  7. data/Gemfile.lock +1 -79
  8. data/README.md +14 -35
  9. data/documentation/configuration.md +12 -11
  10. data/documentation/message_distribution.md +1 -2
  11. data/documentation/tasks.md +1 -2
  12. data/exe/rsmp +1 -2
  13. data/lib/rsmp/cli.rb +33 -3
  14. data/lib/rsmp/convert/export/json_schema/outputs.rb +18 -0
  15. data/lib/rsmp/convert/export/json_schema/values.rb +1 -1
  16. data/lib/rsmp/convert/export/json_schema.rb +14 -6
  17. data/lib/rsmp/schema/core_sxl_resolution.rb +69 -0
  18. data/lib/rsmp/schema/validation.rb +57 -0
  19. data/lib/rsmp/schema.rb +40 -67
  20. data/lib/rsmp/version.rb +1 -1
  21. data/schemas/tlc/1.0.10/rsmp.json +2 -1
  22. data/schemas/tlc/1.0.10/sxl.yaml +1 -0
  23. data/schemas/tlc/1.0.10/sxl_index.json +356 -0
  24. data/schemas/tlc/1.0.13/rsmp.json +2 -1
  25. data/schemas/tlc/1.0.13/sxl.yaml +1 -0
  26. data/schemas/tlc/1.0.13/sxl_index.json +436 -0
  27. data/schemas/tlc/1.0.14/rsmp.json +2 -1
  28. data/schemas/tlc/1.0.14/sxl.yaml +1 -0
  29. data/schemas/tlc/1.0.14/sxl_index.json +468 -0
  30. data/schemas/tlc/1.0.15/rsmp.json +2 -1
  31. data/schemas/tlc/1.0.15/sxl.yaml +1 -0
  32. data/schemas/tlc/1.0.15/sxl_index.json +508 -0
  33. data/schemas/tlc/1.0.7/rsmp.json +2 -1
  34. data/schemas/tlc/1.0.7/sxl.yaml +1 -0
  35. data/schemas/tlc/1.0.7/sxl_index.json +356 -0
  36. data/schemas/tlc/1.0.8/rsmp.json +2 -1
  37. data/schemas/tlc/1.0.8/sxl.yaml +1 -0
  38. data/schemas/tlc/1.0.8/sxl_index.json +356 -0
  39. data/schemas/tlc/1.0.9/rsmp.json +2 -1
  40. data/schemas/tlc/1.0.9/sxl.yaml +1 -0
  41. data/schemas/tlc/1.0.9/sxl_index.json +356 -0
  42. data/schemas/tlc/1.1.0/rsmp.json +2 -1
  43. data/schemas/tlc/1.1.0/sxl.yaml +1 -0
  44. data/schemas/tlc/1.1.0/sxl_index.json +572 -0
  45. data/schemas/tlc/1.2.0/rsmp.json +2 -1
  46. data/schemas/tlc/1.2.0/sxl.yaml +1 -0
  47. data/schemas/tlc/1.2.0/sxl_index.json +571 -0
  48. data/schemas/tlc/1.2.1/rsmp.json +2 -1
  49. data/schemas/tlc/1.2.1/sxl.yaml +1 -0
  50. data/schemas/tlc/1.2.1/sxl_index.json +571 -0
  51. data/schemas/tlc/1.3.0/defs/definitions.json +86 -25
  52. data/schemas/tlc/1.3.0/rsmp.json +2 -1
  53. data/schemas/tlc/1.3.0/statuses/S0024.json +2 -1
  54. data/schemas/tlc/1.3.0/sxl.yaml +1 -0
  55. data/schemas/tlc/1.3.0/sxl_index.json +578 -0
  56. metadata +14 -4
  57. data/.github/copilot-instructions.md +0 -33
  58. data/.rspec +0 -1
  59. data/cucumber.yml +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c85b2875cd3f63dfb4d146d1a3d4718f666444d4773eb6a2b8feb21bd309030
4
- data.tar.gz: f7492b775476f78362c8317bb76f4a76eefd8720afc8657c705bb23991e24fec
3
+ metadata.gz: a71c7f02cc5b94aa0b4d4f4b7ad87fbd0406c3b8cd1ceacfc7fa12aad6ccaba5
4
+ data.tar.gz: 7de215575f1e79a7494c629ae9211f86b31077586f348c12041590d70f859605
5
5
  SHA512:
6
- metadata.gz: 4cf58cbf2d93df79e9accef572eb767dcac614499c8aec296369d9e63e57dffe0f387c28906d537bc60d1008dda823e58ab132f9c65c06d2e7e0fb069d3c687a
7
- data.tar.gz: '096b1798a92696a5d1db4859d90cdee8b3fd9a3ac3c75028988ed4deb698c7f18b1d1d368f85173ae1c9093f45652c45ba9a800536dfa760dbd4eb7ce67427c5'
6
+ metadata.gz: 5580aa1b51574cc1f81feb53040b44c8b540287e15f09a966c5dbb90ce16d54a67be73043a6882ae93075636163f0d7f72e181716fd95d691ade8d8e899b722e
7
+ data.tar.gz: e7aa473295a9ed35350f5ae4b04e114959f68322d1e19a6ef88ec8743b46af5b448b2e4a9dd2f9b95f531b719c5952af0e1bd73af88ebb63c896492777817eb5
@@ -1,4 +1,4 @@
1
- # This workflow runs RSpec tests
1
+ # This workflow runs Rubocop
2
2
 
3
3
  name: Rubocop
4
4
  on: [push]
@@ -1,4 +1,4 @@
1
- # This workflow runs RSpec tests
1
+ # This workflow runs Sus tests
2
2
 
3
3
  name: sus
4
4
  on: [push]
data/.gitignore CHANGED
@@ -1,5 +1,4 @@
1
1
  /.DS_Store
2
- /.rspec_status
3
2
  /ignore/
4
3
  /log/
5
4
  /.bundle/
data/CHANGELOG.md CHANGED
@@ -658,4 +658,15 @@ Initial release.
658
658
  - rsmp_schema folded into this gem
659
659
  - all schema files vendored into this repo, with rake task to update them
660
660
  - moved `rake regenerate` to `rsmp schema generate` CLI
661
-
661
+
662
+ ## 0.43.1
663
+ - update gems
664
+
665
+ ## 0.43.2
666
+ - maintenance release
667
+
668
+ ## 0.45.0
669
+ - initial suport for core 3.3.0 and TLC SXL 1.3.0
670
+ - fix issues with config normalization
671
+ - make collector cancellation raise pending errors
672
+ - replace Cucumber/Aruba CLI tests with sus tests that call the Thor CLI directly
data/Gemfile CHANGED
@@ -5,9 +5,7 @@ source 'https://rubygems.org'
5
5
  gemspec
6
6
 
7
7
  group :development do
8
- gem 'aruba', '~> 2.3'
9
8
  gem 'bundler', '~> 4.0'
10
- gem 'cucumber', '~> 9.2'
11
9
  gem 'rake', '~> 13.2'
12
10
  gem 'rubocop'
13
11
  gem 'rubocop-rake', require: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.45.0)
4
+ rsmp (0.45.1)
5
5
  async (~> 2.39)
6
6
  colorize (~> 1.1)
7
7
  io-endpoint (~> 0.17)
@@ -14,13 +14,6 @@ PATH
14
14
  GEM
15
15
  remote: https://rubygems.org/
16
16
  specs:
17
- aruba (2.4.1)
18
- bundler (>= 1.17)
19
- contracts (>= 0.16.0, < 0.18.0)
20
- cucumber (>= 8.0, < 12.0)
21
- irb (~> 1.16)
22
- rspec-expectations (>= 3.4, < 5.0)
23
- thor (~> 1.0)
24
17
  ast (2.4.3)
25
18
  async (2.39.0)
26
19
  console (~> 1.29)
@@ -29,59 +22,19 @@ GEM
29
22
  metrics (~> 0.12)
30
23
  traces (~> 0.18)
31
24
  bigdecimal (4.1.2)
32
- builder (3.3.0)
33
25
  colorize (1.1.0)
34
26
  console (1.36.0)
35
27
  fiber-annotation
36
28
  fiber-local (~> 1.1)
37
29
  json
38
- contracts (0.17.3)
39
- cucumber (9.2.1)
40
- builder (~> 3.2)
41
- cucumber-ci-environment (> 9, < 11)
42
- cucumber-core (> 13, < 14)
43
- cucumber-cucumber-expressions (~> 17.0)
44
- cucumber-gherkin (> 24, < 28)
45
- cucumber-html-formatter (> 20.3, < 22)
46
- cucumber-messages (> 19, < 25)
47
- diff-lcs (~> 1.5)
48
- mini_mime (~> 1.1)
49
- multi_test (~> 1.1)
50
- sys-uname (~> 1.2)
51
- cucumber-ci-environment (10.0.1)
52
- cucumber-core (13.0.3)
53
- cucumber-gherkin (>= 27, < 28)
54
- cucumber-messages (>= 20, < 23)
55
- cucumber-tag-expressions (> 5, < 7)
56
- cucumber-cucumber-expressions (17.1.0)
57
- bigdecimal
58
- cucumber-gherkin (27.0.0)
59
- cucumber-messages (>= 19.1.4, < 23)
60
- cucumber-html-formatter (21.15.1)
61
- cucumber-messages (> 19, < 28)
62
- cucumber-messages (22.0.0)
63
- cucumber-tag-expressions (6.1.2)
64
- date (3.5.1)
65
- diff-lcs (1.6.2)
66
- erb (6.0.4)
67
- ffi (1.17.4-arm64-darwin)
68
- ffi (1.17.4-x64-mingw-ucrt)
69
- ffi (1.17.4-x86_64-darwin)
70
- ffi (1.17.4-x86_64-linux-gnu)
71
30
  fiber-annotation (0.2.0)
72
31
  fiber-local (1.1.0)
73
32
  fiber-storage
74
33
  fiber-storage (1.0.1)
75
34
  hana (1.3.7)
76
- io-console (0.8.2)
77
35
  io-endpoint (0.17.2)
78
36
  io-event (1.16.1)
79
37
  io-stream (0.13.0)
80
- irb (1.18.0)
81
- pp (>= 0.6.0)
82
- prism (>= 1.3.0)
83
- rdoc (>= 4.0.0)
84
- reline (>= 0.4.2)
85
38
  json (2.19.8)
86
39
  json_schemer (2.5.0)
87
40
  bigdecimal
@@ -91,36 +44,17 @@ GEM
91
44
  language_server-protocol (3.17.0.5)
92
45
  lint_roller (1.1.0)
93
46
  logger (1.7.0)
94
- memoist3 (1.0.0)
95
47
  metrics (0.15.0)
96
- mini_mime (1.1.5)
97
- multi_test (1.1.0)
98
48
  ostruct (0.6.3)
99
49
  parallel (2.1.0)
100
50
  parser (3.3.11.1)
101
51
  ast (~> 2.4.1)
102
52
  racc
103
- pp (0.6.3)
104
- prettyprint
105
- prettyprint (0.2.0)
106
53
  prism (1.9.0)
107
- psych (5.4.0)
108
- date
109
- stringio
110
54
  racc (1.8.1)
111
55
  rainbow (3.1.1)
112
56
  rake (13.4.2)
113
- rdoc (7.2.0)
114
- erb
115
- psych (>= 4.0.0)
116
- tsort
117
57
  regexp_parser (2.12.0)
118
- reline (0.6.3)
119
- io-console (~> 0.5)
120
- rspec-expectations (3.13.5)
121
- diff-lcs (>= 1.2.0, < 2.0)
122
- rspec-support (~> 3.13.0)
123
- rspec-support (3.13.7)
124
58
  rubocop (1.87.0)
125
59
  json (~> 2.3)
126
60
  language_server-protocol (~> 3.17.0.2)
@@ -140,26 +74,16 @@ GEM
140
74
  rubocop (>= 1.72.1)
141
75
  ruby-progressbar (1.13.0)
142
76
  simpleidn (0.2.3)
143
- stringio (3.2.0)
144
77
  sus (0.37.0)
145
78
  sus-fixtures-async (0.2.0)
146
79
  async
147
80
  sus (~> 0.10)
148
- sys-uname (1.5.1)
149
- ffi (~> 1.1)
150
- memoist3 (~> 1.0.0)
151
- sys-uname (1.5.1-universal-mingw32)
152
- ffi (~> 1.1)
153
- memoist3 (~> 1.0.0)
154
- win32ole
155
81
  thor (1.5.0)
156
82
  timecop (0.9.11)
157
83
  traces (0.18.2)
158
- tsort (0.2.0)
159
84
  unicode-display_width (3.2.0)
160
85
  unicode-emoji (~> 4.1)
161
86
  unicode-emoji (4.2.0)
162
- win32ole (1.9.3)
163
87
 
164
88
  PLATFORMS
165
89
  arm64-darwin-23
@@ -169,9 +93,7 @@ PLATFORMS
169
93
  x86_64-linux
170
94
 
171
95
  DEPENDENCIES
172
- aruba (~> 2.3)
173
96
  bundler (~> 4.0)
174
- cucumber (~> 9.2)
175
97
  rake (~> 13.2)
176
98
  rsmp!
177
99
  rubocop
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # rsmp
2
2
  This is a Ruby implementation of the RSMP protocol, including:
3
3
  - RSMP classes that can be used to build tests or other RSMP tools
4
- - Command-line tools for quickly running RSMP supervisors or sites and view messages exchanged
4
+ - Command-line tools for quickly running RSMP supervisors or sites and viewing exchanged messages
5
+ - Vendored RSMP Core and SXL JSON Schemas, including Core 3.3.0 and TLC SXL 1.3.0
5
6
 
6
7
  ## Installation
7
- You need a recent version of Ruby intalled. 4.0.5 or later is recommended.
8
+ You need Ruby 3.4 or later.
8
9
 
9
10
  Install required gems:
10
11
 
@@ -62,19 +63,19 @@ Async do |task|
62
63
  site = RSMP::Site.new(site_settings:settings)
63
64
  site.start # run concurrently since we're inside an Async block
64
65
  loop do
65
- task.sleep 1 # use task.sleep() instead of sleep() to avoid blocking
66
+ sleep 1 # sleep yields to other fibers inside Async
66
67
  puts "Latest archive item: #{site.archive.items.last}"
67
68
  end
68
69
  end
69
70
  ```
70
71
 
71
- Use task.show_hierarchy to see what task are created, task.stop() to stop all sites and supervisors running inside it:
72
+ Use task.show_hierarchy to see what task are created and task.stop() to stop all sites and supervisors running inside it:
72
73
 
73
74
  ```ruby
74
75
  require 'rsmp'
75
76
  Async do |task|
76
77
  RSMP::Site.new.start
77
- task.sleep 1
78
+ sleep 1
78
79
  task.print_hierarchy
79
80
  task.stop # stop everything inside this Async block
80
81
  end
@@ -103,7 +104,7 @@ archive = RSMP::Archive.new
103
104
  Async do |task|
104
105
  RSMP::Supervisor.new(archive:archive,logger:logger).start
105
106
  RSMP::Site.new(archive:archive,logger:logger).start
106
- task.sleep 0.1
107
+ sleep 0.1
107
108
  task.stop
108
109
  end
109
110
 
@@ -124,8 +125,8 @@ RN+SU0001 RN+SI0001 --> f8c7 Received Version message for sites [RN+SI
124
125
  ```
125
126
 
126
127
  ### JSON Schema validation
127
- All messages sent and received are validated against the copied RSMP JSON Schemas maintained in
128
- `rsmp_core`.
128
+ All messages sent and received are validated against the vendored RSMP JSON Schemas maintained in
129
+ the RSMP Core and SXL source repositories.
129
130
 
130
131
  Core and SXL schemas are selected with a flat map:
131
132
 
@@ -183,35 +184,13 @@ Use ```--help <command>``` to get a list of available options.
183
184
  Use ```--config <path>``` or ```--options <path>``` to point to a .yaml config file, controlling things like IP adresses, ports, and log output. Examples of config files can be found the folder ```config/```.
184
185
 
185
186
  ## Tests
186
- ### RSpec
187
- RSpec tests are located in spec/. The tests will start supervisor and sites to test communication, but will do so on port 13111, rather than the usual port 12111, to avoid inferference with other RMSP processes running locally.
187
+ ### Sus
188
+ Sus tests are located in test/. Some tests start supervisors and sites to test communication, but will do so on port 13111, rather than the usual port 12111, to avoid interference with other RSMP processes running locally. CLI tests call the Thor CLI class directly, without spawning a separate `rsmp` process.
188
189
 
189
- Note that these tests are NOT intented for testing external equipment or systems. The tests are for validating the code in this repository. To test external equipment or systems use the rsmp_validator tool.
190
+ Note that these tests are NOT intended for testing external equipment or systems. The tests are for validating the code in this repository. To test external equipment or systems use the rsmp_validator tool.
190
191
 
191
192
  ```console
192
- $ rspec
193
- .........................
193
+ $ bundle exec sus
194
+ 301 passed out of 301 total (830 assertions)
194
195
 
195
- Finished in 0.12746 seconds (files took 0.6571 seconds to load)
196
- 25 examples, 0 failures
197
-
198
- ```
199
-
200
- # Cucumber
201
- Cucumber is used to test the CLI binaries.
202
-
203
- ```console
204
- $ cucumber
205
- Feature: Help
206
-
207
- Scenario: Displaying help # features/help.feature:3
208
- When I run `rsmp help` # aruba-0.14.11/lib/aruba/cucumber/command.rb:6
209
- Then it should pass with "Commands:" # aruba-0.14.11/lib/aruba/cucumber/command.rb:271
210
-
211
- Feature: Run site
212
- ...
213
-
214
- 7 scenarios (7 passed)
215
- 28 steps (28 passed)
216
- 0m7.036s
217
196
  ```
@@ -41,8 +41,8 @@ site_id: RN+SI0001
41
41
  supervisors:
42
42
  - ip: 127.0.0.1
43
43
  port: 12111
44
- sxl: tlc
45
- sxl_version: "1.2.1"
44
+ sxls:
45
+ tlc: "1.3.0"
46
46
  components:
47
47
  main:
48
48
  TC:
@@ -55,7 +55,8 @@ log:
55
55
  ```yaml
56
56
  port: 12111
57
57
  default:
58
- sxl: tlc
58
+ sxls:
59
+ tlc: "1.3.0"
59
60
  intervals:
60
61
  timer: 0.1
61
62
  watchdog: 0.1
@@ -63,8 +64,8 @@ log:
63
64
  json: true
64
65
  sites:
65
66
  TLC001:
66
- sxl: tlc
67
- sxl_version: "1.2.1"
67
+ sxls:
68
+ tlc: "1.3.0"
68
69
  intervals:
69
70
  timer: 0.1
70
71
  watchdog: 0.1
@@ -76,7 +77,7 @@ sites:
76
77
  TC:
77
78
  ```
78
79
 
79
- Per-site configuration follows the supervisor-side site schema (`lib/rsmp/options/schemas/supervisor_site.json`). Each site entry must include an `sxl` value; if `sxl` is missing the supervisor will raise a `RSMP::ConfigurationError` on startup.
80
+ Per-site configuration follows the supervisor-side site schema (`lib/rsmp/options/schemas/supervisor_site.json`). Each site entry can define an `sxls` map, or inherit it from `default`. The SXL name `core` is reserved for the RSMP core schema and cannot be used as an SXL key.
80
81
 
81
82
  ## Supervisor settings
82
83
 
@@ -90,8 +91,8 @@ Top-level supervisor settings
90
91
  - `site_id`: string — optional site identifier for the supervisor itself.
91
92
  - `max_sites`: integer — limit concurrent connected sites.
92
93
  - `default`: object — default settings applied to sites that don't have a specific `sites` entry. Contains keys:
93
- - `sxl`: string — default SXL type for default sites (e.g. `tlc`).
94
- - `sxl_version`, `core_version`: strings for version hints.
94
+ - `sxls`: object — default SXL versions for default sites, for example `{ "tlc": "1.3.0" }`.
95
+ - `core_version`: string for the accepted RSMP Core version.
95
96
  - `intervals`: object with `timer`, `watchdog` (numbers, seconds).
96
97
  - `timeouts`: object with `watchdog`, `acknowledgement` (numbers, seconds).
97
98
  - `log`: object — log settings (see `log_settings` elsewhere in docs).
@@ -101,13 +102,13 @@ Top-level supervisor settings
101
102
 
102
103
  Each key under `sites` is a site id (for example `TLC001`) and the value is the supervisor-side configuration for that site. These settings tell the supervisor how to handle incoming connections from that specific site (which SXL/schema to use, per-site timeouts, component layout, etc.). Per-site configuration follows the supervisor-side schema at `lib/rsmp/options/schemas/supervisor_site.json`.
103
104
 
104
- If a connecting site's id is not present under `sites`, the supervisor will fall back to the `default` settings. The runtime configuration check will raise `RSMP::ConfigurationError` if a site entry is present but missing the required `sxl` key.
105
+ If a connecting site's id is not present under `sites`, the supervisor will fall back to the `default` settings. The runtime configuration check will raise `RSMP::ConfigurationError` if neither the site entry nor the default settings provide usable SXL information.
105
106
 
106
107
 
107
108
  Common per-site keys
108
109
 
109
- - `sxl` (string, required): the SXL type to use for this site (for example `tlc`). The supervisor will attempt to load the corresponding schemas for this SXL.
110
- - `sxl_version` (string): preferred SXL version (informational; runtime version comes from the site's Version message).
110
+ - `sxls` (object): SXL versions to use for this site, keyed by SXL name, for example `tlc: "1.3.0"`. The supervisor will attempt to load the corresponding schemas for these SXLs.
111
+ - `core_version` (string): accepted RSMP Core version for this site.
111
112
  - `type` (string): optional human-readable type identifier.
112
113
  - `site_id` (string): explicit site identifier (if different from the mapping key).
113
114
  - `supervisors` (array): list of supervisor endpoints (objects with `ip` and `port`). Useful for reverse mappings or local-site configs.
@@ -4,7 +4,7 @@ Proxy - - Distributor --> Receivers
4
4
 
5
5
  A proxy distributes messages to receivers, when they are installed.
6
6
 
7
- Collectors are special receiverws that waits for specific message, and are used to implement methods for waiting for RMSP responses, statuses, alarms, etc.
7
+ Collectors are special receivers that wait for specific messages, and are used to implement methods for waiting for RSMP responses, statuses, alarms, etc.
8
8
 
9
9
  Note that Archive is not a receiver, and does not receive messages via the Distributor. Instead the Archive gets and stores messages via the log() interface in the Logging module. The reason is that the items that the Archive and the Logger contain other data as well as the message, like error messages, warnings, text descriptions, colors codes, etc. The Distributor and Receiver handles only Message objects.
10
10
 
@@ -20,4 +20,3 @@ the client receives the collection.
20
20
 
21
21
  ## Proxy
22
22
  A proxy includes the Distributor module and distributes each message to receivers after processing it.
23
-
@@ -145,5 +145,4 @@ If you mark an Async task with `transient: true` when you created it, that task
145
145
 
146
146
  Transient tasks can be used to cleanup, but using an `ensure` block in the transient task. When you call `stop` on a task, an Async::Stop exception is raised, which will run the code in the `ensure` block.
147
147
 
148
- Some of the RSpec tests runs tests in in transient task. As soon as the main test code is complete, any subtasks like Sites or Supervisors that might otherwise prevent the test from completing, will be stopped automatically.
149
-
148
+ Some sus tests run in transient tasks. As soon as the main test code is complete, any subtasks like Sites or Supervisors that might otherwise prevent the test from completing will be stopped automatically.
data/exe/rsmp CHANGED
@@ -1,4 +1,3 @@
1
1
  #!/usr/bin/env ruby
2
- require 'bundler/setup'
3
- require 'rsmp/cli'
2
+ require_relative '../lib/rsmp/cli'
4
3
  RSMP::CLI.start
data/lib/rsmp/cli.rb CHANGED
@@ -44,6 +44,9 @@ module RSMP
44
44
  run_site(site_class, settings, log_settings)
45
45
  rescue Interrupt
46
46
  # ctrl-c
47
+ rescue RSMP::ConfigurationError => e
48
+ puts "Error: #{e}"
49
+ exit 1
47
50
  rescue StandardError => e
48
51
  puts "Uncaught error: #{e}"
49
52
  puts caller.join("\n")
@@ -123,15 +126,42 @@ module RSMP
123
126
  end
124
127
 
125
128
  def parse_supervisors(settings)
129
+ default_port = default_supervisor_port(settings)
126
130
  settings['supervisors'] = []
127
131
  options[:supervisors].split(',').each do |supervisor|
128
- ip, port = supervisor.split ':'
129
- ip = '127.0.0.1' if ip.empty?
130
- port = '12111' if port.empty?
132
+ ip, port = parse_supervisor(supervisor, default_port)
131
133
  settings['supervisors'] << { 'ip' => ip, 'port' => port }
132
134
  end
133
135
  end
134
136
 
137
+ def parse_supervisor(supervisor, default_port)
138
+ parts = supervisor.split(':', -1)
139
+
140
+ case parts.size
141
+ when 1
142
+ ip = parts.first
143
+ port = default_port
144
+ when 2
145
+ ip, port = parts
146
+ else
147
+ raise RSMP::ConfigurationError,
148
+ "Invalid supervisor #{supervisor.inspect}, expected ip[:port]"
149
+ end
150
+
151
+ if port.nil? || port.to_s.empty?
152
+ raise RSMP::ConfigurationError,
153
+ "Invalid supervisor #{supervisor.inspect}, expected ip[:port] with a non-empty port"
154
+ end
155
+
156
+ ip = '127.0.0.1' if ip.empty?
157
+ [ip, port]
158
+ end
159
+
160
+ def default_supervisor_port(settings)
161
+ settings.dig('supervisors', 0, 'port') ||
162
+ site_options_class.new.to_h.dig('supervisors', 0, 'port')
163
+ end
164
+
135
165
  def determine_site_class(settings)
136
166
  case site_type(settings)
137
167
  when 'tlc'
@@ -103,6 +103,23 @@ module RSMP
103
103
  out["commands/#{key}.json"] = output_json json
104
104
  end
105
105
 
106
+ def self.output_sxl_index(out, sxl)
107
+ out['sxl_index.json'] = output_json({
108
+ 'meta' => sxl[:meta],
109
+ 'statuses' => index_items(sxl[:statuses]),
110
+ 'commands' => index_items(sxl[:commands]),
111
+ 'alarms' => index_items(sxl[:alarms])
112
+ })
113
+ end
114
+
115
+ def self.index_items(items)
116
+ items.keys.sort.to_h do |key|
117
+ [key, {
118
+ 'arguments' => (items[key]['arguments'] || {}).keys.sort
119
+ }]
120
+ end
121
+ end
122
+
106
123
  # output the json schema root
107
124
  def self.output_root(out, meta)
108
125
  json = {
@@ -113,6 +130,7 @@ module RSMP
113
130
  'allOf' => root_type_rules
114
131
  }
115
132
  json['prefix'] = meta['prefix'] if meta['prefix']
133
+ json['minimum_core_version'] = meta['minimum_core_version'] if meta['minimum_core_version']
116
134
  out['rsmp.json'] = output_json json
117
135
  end
118
136
 
@@ -76,7 +76,7 @@ module RSMP
76
76
  out['pattern'] = /(?-mix:^(#{value_list})(?:,(#{value_list}))*$)/
77
77
  end
78
78
 
79
- puts "Warning: Pattern not support for lists: #{item.inspect}" if item['pattern']
79
+ handle_pattern item, out
80
80
  end
81
81
 
82
82
  # convert yaml values to json schema enum
@@ -26,10 +26,17 @@ module RSMP
26
26
  JSON.generate(item, JSON_OPTIONS)
27
27
  end
28
28
 
29
- # Path to definitions.json for the latest bundled core schema version
30
- def self.definitions_source
31
- version = RSMP::Schema.latest_core_version
32
- File.expand_path("../../../../schemas/core/#{version}/definitions.json", __dir__)
29
+ def self.minimum_core_version(sxl)
30
+ sxl.dig(:meta, 'minimum_core_version') || RSMP::Schema.latest_core_version
31
+ end
32
+
33
+ # Path to definitions.json for the fallback bundled core schema version
34
+ def self.definitions_source(sxl)
35
+ version = minimum_core_version(sxl)
36
+ path = File.expand_path("../../../../schemas/core/#{version}/definitions.json", __dir__)
37
+ raise "Missing core definitions for RSMP #{version}" unless File.exist?(path)
38
+
39
+ path
33
40
  end
34
41
 
35
42
  # generate the json schema from a string containing yaml
@@ -39,6 +46,7 @@ module RSMP
39
46
  output_alarms out, sxl[:alarms]
40
47
  output_statuses out, sxl[:statuses]
41
48
  output_commands out, sxl[:commands]
49
+ output_sxl_index out, sxl
42
50
  out
43
51
  end
44
52
 
@@ -53,8 +61,8 @@ module RSMP
53
61
  # Copy definitions.json so each version folder is self-contained
54
62
  defs_dest = File.join(folder, 'defs', 'definitions.json')
55
63
  FileUtils.mkdir_p File.dirname(defs_dest)
56
- source = definitions_source
57
- FileUtils.cp source, defs_dest if File.exist?(source)
64
+ source = definitions_source(sxl)
65
+ FileUtils.cp source, defs_dest
58
66
  end
59
67
  end
60
68
  end
@@ -0,0 +1,69 @@
1
+ module RSMP
2
+ # Provides JSON Schema validation for RSMP messages across core and SXL versions.
3
+ module Schema
4
+ def self.clear_core_sxl_schemas(type = nil, version = nil)
5
+ @core_sxl_schemas ||= {}
6
+ return @core_sxl_schemas.clear unless type
7
+
8
+ type = type.to_sym
9
+ @core_sxl_schemas.delete_if do |(cached_type, cached_version, _core_version), _schema|
10
+ cached_type == type && (!version || cached_version == version.to_s)
11
+ end
12
+ end
13
+
14
+ def self.schema_core_version(schemas)
15
+ schemas[:core] || schemas['core']
16
+ end
17
+
18
+ def self.validate_resolved_sxl(message, resolved, schemas, options)
19
+ type, version = resolved
20
+ schema = find_core_sxl_schema! type, version, schema_core_version(schemas), options
21
+ validate_using_schema(message, schema)
22
+ end
23
+
24
+ def self.find_core_sxl_schema!(type, version, core_version, options = {})
25
+ raise ArgumentError, 'core version missing' unless core_version
26
+
27
+ version = sanitize_version(version.to_s) if options[:lenient]
28
+ core_version = sanitize_version(core_version.to_s) if options[:lenient]
29
+ find_schema! type, version
30
+ find_schema! :core, core_version
31
+
32
+ key = [type.to_sym, version.to_s, core_version.to_s]
33
+ @core_sxl_schemas ||= {}
34
+ @core_sxl_schemas[key] ||= build_core_sxl_schema(type, version, core_version)
35
+ end
36
+
37
+ def self.build_core_sxl_schema(type, version, core_version)
38
+ schema_path = @schema_paths&.dig(type.to_sym, version.to_s)
39
+ raise UnknownSchemaVersionError, "Unknown schema version #{type} #{version}" unless schema_path
40
+
41
+ file_path = File.join(schema_path, 'rsmp.json')
42
+ JSONSchemer.schema(
43
+ Pathname.new(file_path),
44
+ ref_resolver: core_sxl_ref_resolver(core_version)
45
+ )
46
+ end
47
+
48
+ def self.core_sxl_ref_resolver(core_version)
49
+ proc do |uri|
50
+ if sxl_definitions_ref?(uri)
51
+ JSON.parse(File.read(core_definitions_path(core_version), encoding: 'UTF-8'))
52
+ else
53
+ JSONSchemer::FILE_URI_REF_RESOLVER.call(uri)
54
+ end
55
+ end
56
+ end
57
+
58
+ def self.sxl_definitions_ref?(uri)
59
+ uri.scheme == 'file' && uri.path.end_with?('/defs/definitions.json')
60
+ end
61
+
62
+ def self.core_definitions_path(core_version)
63
+ path = File.join(schema_root_path, 'core', core_version.to_s, 'definitions.json')
64
+ return path if File.exist?(path)
65
+
66
+ raise UnknownSchemaVersionError, "Missing core definitions for RSMP #{core_version}"
67
+ end
68
+ end
69
+ end