rsmp 0.43.2 → 0.45.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.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +11 -11
  3. data/README.md +19 -3
  4. data/Rakefile +2 -2
  5. data/config/supervisor.yaml +2 -1
  6. data/config/tlc.yaml +2 -2
  7. data/lib/rsmp/cli.rb +29 -5
  8. data/lib/rsmp/component/component.rb +0 -4
  9. data/lib/rsmp/component/component_base.rb +15 -2
  10. data/lib/rsmp/component/component_proxy.rb +1 -1
  11. data/lib/rsmp/component/components.rb +22 -1
  12. data/lib/rsmp/convert/export/json_schema/outputs.rb +1 -0
  13. data/lib/rsmp/convert/export/json_schema/values.rb +6 -4
  14. data/lib/rsmp/convert/export/json_schema.rb +7 -3
  15. data/lib/rsmp/helpers/deep_merge.rb +2 -2
  16. data/lib/rsmp/message.rb +32 -0
  17. data/lib/rsmp/node/site/site.rb +34 -10
  18. data/lib/rsmp/node/supervisor/modules/configuration.rb +32 -5
  19. data/lib/rsmp/node/supervisor/modules/connection.rb +0 -2
  20. data/lib/rsmp/node/supervisor/supervisor.rb +0 -7
  21. data/lib/rsmp/options/options.rb +55 -6
  22. data/lib/rsmp/options/schemas/site.json +6 -3
  23. data/lib/rsmp/options/schemas/supervisor.json +5 -2
  24. data/lib/rsmp/options/schemas/supervisor_site.json +5 -2
  25. data/lib/rsmp/options/site_options.rb +3 -2
  26. data/lib/rsmp/options/supervisor_options.rb +3 -1
  27. data/lib/rsmp/proxy/modules/acknowledgements.rb +2 -0
  28. data/lib/rsmp/proxy/modules/receive.rb +5 -2
  29. data/lib/rsmp/proxy/modules/state.rb +1 -0
  30. data/lib/rsmp/proxy/modules/versions.rb +90 -15
  31. data/lib/rsmp/proxy/proxy.rb +52 -3
  32. data/lib/rsmp/proxy/site/modules/status.rb +5 -3
  33. data/lib/rsmp/proxy/site/site_proxy.rb +68 -35
  34. data/lib/rsmp/proxy/site/sxl_selection.rb +54 -0
  35. data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +54 -18
  36. data/lib/rsmp/schema/message_resolution.rb +104 -0
  37. data/lib/rsmp/schema.rb +104 -22
  38. data/lib/rsmp/schema_error.rb +7 -1
  39. data/lib/rsmp/sxl/interface.rb +48 -0
  40. data/lib/rsmp/sxl/registry.rb +55 -0
  41. data/lib/rsmp/sxl/site_interface.rb +10 -0
  42. data/lib/rsmp/sxl/supervisor_interface.rb +21 -0
  43. data/lib/rsmp/tlc/detector_logic.rb +2 -2
  44. data/lib/rsmp/tlc/signal_group.rb +2 -2
  45. data/lib/rsmp/tlc/site_interface.rb +10 -0
  46. data/lib/rsmp/tlc/{traffic_controller_proxy.rb → supervisor_interface.rb} +19 -34
  47. data/lib/rsmp/tlc/traffic_controller.rb +10 -2
  48. data/lib/rsmp/tlc/traffic_controller_site.rb +4 -2
  49. data/lib/rsmp/tlc.rb +10 -0
  50. data/lib/rsmp/version.rb +1 -1
  51. data/lib/rsmp.rb +8 -1
  52. data/rsmp.gemspec +5 -5
  53. data/schemas/core/3.3.0/aggregated_status.json +25 -0
  54. data/schemas/core/3.3.0/aggregated_status_request.json +9 -0
  55. data/schemas/core/3.3.0/alarm.json +71 -0
  56. data/schemas/core/3.3.0/alarm_acknowledge.json +11 -0
  57. data/schemas/core/3.3.0/alarm_issue.json +44 -0
  58. data/schemas/core/3.3.0/alarm_request.json +3 -0
  59. data/schemas/core/3.3.0/alarm_suspend_resume.json +3 -0
  60. data/schemas/core/3.3.0/alarm_suspended_resumed.json +44 -0
  61. data/schemas/core/3.3.0/command_request.json +24 -0
  62. data/schemas/core/3.3.0/command_response.json +35 -0
  63. data/schemas/core/3.3.0/component_list.json +24 -0
  64. data/schemas/core/3.3.0/core.json +40 -0
  65. data/schemas/core/3.3.0/definitions.json +133 -0
  66. data/schemas/core/3.3.0/message_ack.json +11 -0
  67. data/schemas/core/3.3.0/message_not_ack.json +15 -0
  68. data/schemas/core/3.3.0/rsmp.json +142 -0
  69. data/schemas/core/3.3.0/status.json +21 -0
  70. data/schemas/core/3.3.0/status_request.json +5 -0
  71. data/schemas/core/3.3.0/status_response.json +41 -0
  72. data/schemas/core/3.3.0/status_subscribe.json +31 -0
  73. data/schemas/core/3.3.0/status_unsubscribe.json +5 -0
  74. data/schemas/core/3.3.0/status_update.json +41 -0
  75. data/schemas/core/3.3.0/version.json +144 -0
  76. data/schemas/core/3.3.0/watchdog.json +9 -0
  77. data/schemas/tlc/1.3.0/alarms/A0001.json +4 -0
  78. data/schemas/tlc/1.3.0/alarms/A0002.json +4 -0
  79. data/schemas/tlc/1.3.0/alarms/A0003.json +4 -0
  80. data/schemas/tlc/1.3.0/alarms/A0004.json +4 -0
  81. data/schemas/tlc/1.3.0/alarms/A0005.json +4 -0
  82. data/schemas/tlc/1.3.0/alarms/A0006.json +4 -0
  83. data/schemas/tlc/1.3.0/alarms/A0007.json +34 -0
  84. data/schemas/tlc/1.3.0/alarms/A0008.json +30 -0
  85. data/schemas/tlc/1.3.0/alarms/A0009.json +4 -0
  86. data/schemas/tlc/1.3.0/alarms/A0010.json +4 -0
  87. data/schemas/tlc/1.3.0/alarms/A0101.json +4 -0
  88. data/schemas/tlc/1.3.0/alarms/A0201.json +35 -0
  89. data/schemas/tlc/1.3.0/alarms/A0202.json +35 -0
  90. data/schemas/tlc/1.3.0/alarms/A0301.json +92 -0
  91. data/schemas/tlc/1.3.0/alarms/A0302.json +115 -0
  92. data/schemas/tlc/1.3.0/alarms/A0303.json +92 -0
  93. data/schemas/tlc/1.3.0/alarms/A0304.json +115 -0
  94. data/schemas/tlc/1.3.0/alarms/alarms.json +287 -0
  95. data/schemas/tlc/1.3.0/commands/M0001.json +92 -0
  96. data/schemas/tlc/1.3.0/commands/M0002.json +69 -0
  97. data/schemas/tlc/1.3.0/commands/M0003.json +69 -0
  98. data/schemas/tlc/1.3.0/commands/M0004.json +51 -0
  99. data/schemas/tlc/1.3.0/commands/M0005.json +69 -0
  100. data/schemas/tlc/1.3.0/commands/M0006.json +69 -0
  101. data/schemas/tlc/1.3.0/commands/M0007.json +51 -0
  102. data/schemas/tlc/1.3.0/commands/M0008.json +87 -0
  103. data/schemas/tlc/1.3.0/commands/M0010.json +51 -0
  104. data/schemas/tlc/1.3.0/commands/M0011.json +51 -0
  105. data/schemas/tlc/1.3.0/commands/M0012.json +51 -0
  106. data/schemas/tlc/1.3.0/commands/M0013.json +51 -0
  107. data/schemas/tlc/1.3.0/commands/M0014.json +69 -0
  108. data/schemas/tlc/1.3.0/commands/M0015.json +69 -0
  109. data/schemas/tlc/1.3.0/commands/M0016.json +51 -0
  110. data/schemas/tlc/1.3.0/commands/M0017.json +51 -0
  111. data/schemas/tlc/1.3.0/commands/M0018.json +69 -0
  112. data/schemas/tlc/1.3.0/commands/M0019.json +87 -0
  113. data/schemas/tlc/1.3.0/commands/M0020.json +87 -0
  114. data/schemas/tlc/1.3.0/commands/M0021.json +51 -0
  115. data/schemas/tlc/1.3.0/commands/M0022.json +249 -0
  116. data/schemas/tlc/1.3.0/commands/M0023.json +51 -0
  117. data/schemas/tlc/1.3.0/commands/M0024.json +33 -0
  118. data/schemas/tlc/1.3.0/commands/M0103.json +72 -0
  119. data/schemas/tlc/1.3.0/commands/M0104.json +141 -0
  120. data/schemas/tlc/1.3.0/commands/command_requests.json +8 -0
  121. data/schemas/tlc/1.3.0/commands/command_responses.json +8 -0
  122. data/schemas/tlc/1.3.0/commands/commands.json +415 -0
  123. data/schemas/tlc/1.3.0/defs/definitions.json +72 -0
  124. data/schemas/tlc/1.3.0/defs/guards.json +24 -0
  125. data/schemas/tlc/1.3.0/rsmp.json +74 -0
  126. data/schemas/tlc/1.3.0/statuses/S0001.json +109 -0
  127. data/schemas/tlc/1.3.0/statuses/S0002.json +36 -0
  128. data/schemas/tlc/1.3.0/statuses/S0003.json +36 -0
  129. data/schemas/tlc/1.3.0/statuses/S0004.json +36 -0
  130. data/schemas/tlc/1.3.0/statuses/S0005.json +72 -0
  131. data/schemas/tlc/1.3.0/statuses/S0006.json +54 -0
  132. data/schemas/tlc/1.3.0/statuses/S0007.json +73 -0
  133. data/schemas/tlc/1.3.0/statuses/S0008.json +73 -0
  134. data/schemas/tlc/1.3.0/statuses/S0009.json +73 -0
  135. data/schemas/tlc/1.3.0/statuses/S0010.json +73 -0
  136. data/schemas/tlc/1.3.0/statuses/S0011.json +73 -0
  137. data/schemas/tlc/1.3.0/statuses/S0012.json +73 -0
  138. data/schemas/tlc/1.3.0/statuses/S0013.json +54 -0
  139. data/schemas/tlc/1.3.0/statuses/S0014.json +55 -0
  140. data/schemas/tlc/1.3.0/statuses/S0015.json +55 -0
  141. data/schemas/tlc/1.3.0/statuses/S0016.json +36 -0
  142. data/schemas/tlc/1.3.0/statuses/S0017.json +36 -0
  143. data/schemas/tlc/1.3.0/statuses/S0018.json +61 -0
  144. data/schemas/tlc/1.3.0/statuses/S0019.json +36 -0
  145. data/schemas/tlc/1.3.0/statuses/S0020.json +54 -0
  146. data/schemas/tlc/1.3.0/statuses/S0021.json +37 -0
  147. data/schemas/tlc/1.3.0/statuses/S0022.json +36 -0
  148. data/schemas/tlc/1.3.0/statuses/S0023.json +37 -0
  149. data/schemas/tlc/1.3.0/statuses/S0024.json +36 -0
  150. data/schemas/tlc/1.3.0/statuses/S0025.json +162 -0
  151. data/schemas/tlc/1.3.0/statuses/S0026.json +36 -0
  152. data/schemas/tlc/1.3.0/statuses/S0027.json +36 -0
  153. data/schemas/tlc/1.3.0/statuses/S0028.json +36 -0
  154. data/schemas/tlc/1.3.0/statuses/S0029.json +36 -0
  155. data/schemas/tlc/1.3.0/statuses/S0030.json +36 -0
  156. data/schemas/tlc/1.3.0/statuses/S0031.json +36 -0
  157. data/schemas/tlc/1.3.0/statuses/S0032.json +73 -0
  158. data/schemas/tlc/1.3.0/statuses/S0033.json +77 -0
  159. data/schemas/tlc/1.3.0/statuses/S0034.json +36 -0
  160. data/schemas/tlc/1.3.0/statuses/S0035.json +49 -0
  161. data/schemas/tlc/1.3.0/statuses/S0091.json +40 -0
  162. data/schemas/tlc/1.3.0/statuses/S0092.json +40 -0
  163. data/schemas/tlc/1.3.0/statuses/S0095.json +36 -0
  164. data/schemas/tlc/1.3.0/statuses/S0096.json +126 -0
  165. data/schemas/tlc/1.3.0/statuses/S0097.json +54 -0
  166. data/schemas/tlc/1.3.0/statuses/S0098.json +72 -0
  167. data/schemas/tlc/1.3.0/statuses/S0201.json +54 -0
  168. data/schemas/tlc/1.3.0/statuses/S0202.json +54 -0
  169. data/schemas/tlc/1.3.0/statuses/S0203.json +54 -0
  170. data/schemas/tlc/1.3.0/statuses/S0204.json +198 -0
  171. data/schemas/tlc/1.3.0/statuses/S0205.json +54 -0
  172. data/schemas/tlc/1.3.0/statuses/S0206.json +54 -0
  173. data/schemas/tlc/1.3.0/statuses/S0207.json +54 -0
  174. data/schemas/tlc/1.3.0/statuses/S0208.json +198 -0
  175. data/schemas/tlc/1.3.0/statuses/statuses.json +787 -0
  176. data/schemas/tlc/1.3.0/sxl.yaml +2296 -0
  177. metadata +144 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ac8dcc5eb14943b0e14c10eb9e6f4c5a42fde921d9db9d05b0a390b4019ecf6
4
- data.tar.gz: 9ef754b98f86317516078a6ce1f263ae82f5f7a697e68ef5fbf50c0976016f47
3
+ metadata.gz: 9c85b2875cd3f63dfb4d146d1a3d4718f666444d4773eb6a2b8feb21bd309030
4
+ data.tar.gz: f7492b775476f78362c8317bb76f4a76eefd8720afc8657c705bb23991e24fec
5
5
  SHA512:
6
- metadata.gz: bd8ca1c5aabffa57015ff7519af57349e40044681dae5765d8e5144e49fef4305ecb688bbb9e271ee14c85b80364ba8111263cab41cf73c82fe4ef4540ad4d49
7
- data.tar.gz: 3b44d60f188df7a54087c8baf3ae56592d3187d348aef3a2f01ecd156802078b59727071a6055da77680700373c0dff7364e0f2c0a0aab958c437f747a508f2e
6
+ metadata.gz: 4cf58cbf2d93df79e9accef572eb767dcac614499c8aec296369d9e63e57dffe0f387c28906d537bc60d1008dda823e58ab132f9c65c06d2e7e0fb069d3c687a
7
+ data.tar.gz: '096b1798a92696a5d1db4859d90cdee8b3fd9a3ac3c75028988ed4deb698c7f18b1d1d368f85173ae1c9093f45652c45ba9a800536dfa760dbd4eb7ce67427c5'
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.43.2)
5
- async (~> 2.32)
4
+ rsmp (0.45.0)
5
+ async (~> 2.39)
6
6
  colorize (~> 1.1)
7
- io-endpoint (~> 0.15)
8
- io-stream (~> 0.10)
7
+ io-endpoint (~> 0.17)
8
+ io-stream (~> 0.13)
9
9
  json_schemer (~> 2.5)
10
- logger (~> 1.6)
10
+ logger (~> 1.7)
11
11
  ostruct (~> 0.6)
12
12
  thor (~> 1.5)
13
13
 
@@ -31,7 +31,7 @@ GEM
31
31
  bigdecimal (4.1.2)
32
32
  builder (3.3.0)
33
33
  colorize (1.1.0)
34
- console (1.35.1)
34
+ console (1.36.0)
35
35
  fiber-annotation
36
36
  fiber-local (~> 1.1)
37
37
  json
@@ -82,7 +82,7 @@ GEM
82
82
  prism (>= 1.3.0)
83
83
  rdoc (>= 4.0.0)
84
84
  reline (>= 0.4.2)
85
- json (2.19.7)
85
+ json (2.19.8)
86
86
  json_schemer (2.5.0)
87
87
  bigdecimal
88
88
  hana (~> 1.3)
@@ -104,7 +104,7 @@ GEM
104
104
  prettyprint
105
105
  prettyprint (0.2.0)
106
106
  prism (1.9.0)
107
- psych (5.3.1)
107
+ psych (5.4.0)
108
108
  date
109
109
  stringio
110
110
  racc (1.8.1)
@@ -121,7 +121,7 @@ GEM
121
121
  diff-lcs (>= 1.2.0, < 2.0)
122
122
  rspec-support (~> 3.13.0)
123
123
  rspec-support (3.13.7)
124
- rubocop (1.86.2)
124
+ rubocop (1.87.0)
125
125
  json (~> 2.3)
126
126
  language_server-protocol (~> 3.17.0.2)
127
127
  lint_roller (~> 1.1.0)
@@ -141,7 +141,7 @@ GEM
141
141
  ruby-progressbar (1.13.0)
142
142
  simpleidn (0.2.3)
143
143
  stringio (3.2.0)
144
- sus (0.36.0)
144
+ sus (0.37.0)
145
145
  sus-fixtures-async (0.2.0)
146
146
  async
147
147
  sus (~> 0.10)
@@ -181,4 +181,4 @@ DEPENDENCIES
181
181
  timecop (~> 0.9)
182
182
 
183
183
  BUNDLED WITH
184
- 4.0.3
184
+ 4.0.13
data/README.md CHANGED
@@ -4,7 +4,7 @@ This is a Ruby implementation of the RSMP protocol, including:
4
4
  - Command-line tools for quickly running RSMP supervisors or sites and view messages exchanged
5
5
 
6
6
  ## Installation
7
- You need a recent version of Ruby intalled. 2.6.3 or later is recommended.
7
+ You need a recent version of Ruby intalled. 4.0.5 or later is recommended.
8
8
 
9
9
  Install required gems:
10
10
 
@@ -124,7 +124,24 @@ RN+SU0001 RN+SI0001 --> f8c7 Received Version message for sites [RN+SI
124
124
  ```
125
125
 
126
126
  ### JSON Schema validation
127
- All messages sent and received will be validated against the core [RSMP JSON Schema](https://github.com/rsmp-nordic/rsmp_schema).
127
+ All messages sent and received are validated against the copied RSMP JSON Schemas maintained in
128
+ `rsmp_core`.
129
+
130
+ Core and SXL schemas are selected with a flat map:
131
+
132
+ ```ruby
133
+ RSMP::Schema.validate(message, {
134
+ core: '3.3.0',
135
+ tlc: '1.3.0'
136
+ })
137
+ ```
138
+
139
+ Sites and supervisors configure one or more SXLs with `sxls`:
140
+
141
+ ```yaml
142
+ sxls:
143
+ tlc: '1.3.0'
144
+ ```
128
145
 
129
146
  ## Command-line tool
130
147
  Tools for easily running RSMP supervisors and sites. The binary is called ```rsmp```.
@@ -198,4 +215,3 @@ Feature: Run site
198
215
  28 steps (28 passed)
199
216
  0m7.036s
200
217
  ```
201
-
data/Rakefile CHANGED
@@ -4,8 +4,8 @@ task :test do
4
4
  sh 'bundle exec sus'
5
5
  end
6
6
 
7
- CORE_VERSIONS = %w[3.1.2 3.1.3 3.1.4 3.1.5 3.2.0 3.2.1 3.2.2].freeze
8
- TLC_VERSIONS = %w[1.0.7 1.0.8 1.0.9 1.0.10 1.0.13 1.0.14 1.0.15 1.1.0 1.2.0 1.2.1].freeze
7
+ CORE_VERSIONS = %w[3.1.2 3.1.3 3.1.4 3.1.5 3.2.0 3.2.1 3.2.2 3.3.0].freeze
8
+ TLC_VERSIONS = %w[1.0.7 1.0.8 1.0.9 1.0.10 1.0.13 1.0.14 1.0.15 1.1.0 1.2.0 1.2.1 1.3.0].freeze
9
9
 
10
10
  # Update vendored schemas from source repos.
11
11
  # Usage: rake schemas:update[/path/to/rsmp_core,/path/to/rsmp_sxl_traffic_lights]
@@ -1,6 +1,7 @@
1
1
  port: 12111
2
2
  default:
3
- sxl: tlc
3
+ sxls:
4
+ tlc: '1.2.1'
4
5
  intervals:
5
6
  timer: 0.1
6
7
  watchdog: 0.1
data/config/tlc.yaml CHANGED
@@ -2,8 +2,8 @@ site_id: RN+SI0001
2
2
  supervisors:
3
3
  - ip: 127.0.0.1
4
4
  port: 12111
5
- sxl: tlc
6
- sxl_version: '1.2.1'
5
+ sxls:
6
+ tlc: '1.2.1'
7
7
  components:
8
8
  main:
9
9
  TC:
data/lib/rsmp/cli.rb CHANGED
@@ -75,7 +75,7 @@ module RSMP
75
75
  return [settings, log_settings] unless options[:config]
76
76
 
77
77
  begin
78
- options_object = RSMP::Site::Options.load_file(options[:config])
78
+ options_object = site_options_class.load_file(options[:config])
79
79
  settings = options_object.to_h
80
80
  log_settings = log_settings.deep_merge(options_object.log_settings)
81
81
  rescue RSMP::ConfigurationError => e
@@ -89,6 +89,15 @@ module RSMP
89
89
 
90
90
  private
91
91
 
92
+ def site_options_class
93
+ case site_type
94
+ when 'tlc'
95
+ RSMP::TLC::TrafficControllerSite::Options
96
+ else
97
+ RSMP::Site::Options
98
+ end
99
+ end
100
+
92
101
  def apply_site_options(settings, log_settings)
93
102
  apply_basic_site_options(settings)
94
103
  parse_supervisors(settings) if options[:supervisors]
@@ -98,7 +107,19 @@ module RSMP
98
107
  def apply_basic_site_options(settings)
99
108
  settings['site_id'] = options[:id] if options[:id]
100
109
  settings['core_version'] = options[:core] || ENV['CORE_VERSION'] if options[:core] || ENV['CORE_VERSION']
101
- settings['sxl_version'] = ENV['SXL_VERSION'] if ENV['SXL_VERSION']
110
+ settings['sxls'] = parse_sxls(ENV['SXLS']) if ENV['SXLS']
111
+ end
112
+
113
+ def parse_sxls(value)
114
+ value.split(',').each_with_object({}) do |item, memo|
115
+ parts = item.split(':')
116
+ unless parts.length == 2
117
+ raise RSMP::ConfigurationError, "Invalid SXLS item #{item.inspect}, expected name:version"
118
+ end
119
+
120
+ name, version = parts
121
+ memo[name] = version
122
+ end
102
123
  end
103
124
 
104
125
  def parse_supervisors(settings)
@@ -112,16 +133,19 @@ module RSMP
112
133
  end
113
134
 
114
135
  def determine_site_class(settings)
115
- site_type = options[:type] || settings['type']
116
- case site_type
136
+ case site_type(settings)
117
137
  when 'tlc'
118
138
  RSMP::TLC::TrafficControllerSite
119
139
  else
120
- puts "Error: Unknown site type #{site_type}"
140
+ puts "Error: Unknown site type #{site_type(settings)}"
121
141
  exit
122
142
  end
123
143
  end
124
144
 
145
+ def site_type(settings = nil)
146
+ options[:type] || settings&.fetch('type', nil)
147
+ end
148
+
125
149
  def run_site(site_class, settings, log_settings)
126
150
  Async do |task|
127
151
  task.annotate 'cli'
@@ -1,10 +1,6 @@
1
1
  module RSMP
2
2
  # RSMP component
3
3
  class Component < ComponentBase
4
- def initialize(node:, id:, ntsoid: nil, xnid: nil, grouped: false)
5
- super
6
- end
7
-
8
4
  def handle_command(command_code, _arg)
9
5
  raise UnknownCommand, "Command #{command_code} not implemented by #{self.class}"
10
6
  end
@@ -1,7 +1,7 @@
1
1
  module RSMP
2
2
  # RSMP component base class.
3
3
  class ComponentBase
4
- attr_reader :c_id, :ntsoid, :xnid, :node, :alarms, :statuses,
4
+ attr_reader :c_id, :component_type, :name, :ntsoid, :xnid, :node, :alarms, :statuses,
5
5
  :aggregated_status, :aggregated_status_bools, :grouped
6
6
 
7
7
  AGGREGATED_STATUS_KEYS = %i[local_control
@@ -13,13 +13,21 @@ module RSMP
13
13
  rest
14
14
  not_connected].freeze
15
15
 
16
- def initialize(node:, id:, ntsoid: nil, xnid: nil, grouped: false)
16
+ def initialize(node:, id:, **attributes)
17
+ type = attributes[:type]
18
+ name = attributes[:name]
19
+ ntsoid = attributes[:ntsoid]
20
+ xnid = attributes[:xnid]
21
+ grouped = attributes.fetch(:grouped, false)
22
+
17
23
  if grouped == false && (ntsoid || xnid)
18
24
  raise RSMP::ConfigurationError,
19
25
  'ntsoid and xnid are only allowed for grouped objects'
20
26
  end
21
27
 
22
28
  @c_id = id
29
+ @component_type = type
30
+ @name = name || id
23
31
  @ntsoid = ntsoid
24
32
  @xnid = xnid
25
33
  @node = node
@@ -32,6 +40,11 @@ module RSMP
32
40
  "#<#{self.class.name}:#{object_id}:#{object_id} c_id:#{@c_id}>"
33
41
  end
34
42
 
43
+ def update_metadata(type: nil, name: nil)
44
+ @component_type = type if type
45
+ @name = name if name
46
+ end
47
+
35
48
  def now
36
49
  node.now
37
50
  end
@@ -1,7 +1,7 @@
1
1
  module RSMP
2
2
  # A proxy to a remote RSMP component.
3
3
  class ComponentProxy < ComponentBase
4
- def initialize(node:, id:, ntsoid: nil, xnid: nil, grouped: false)
4
+ def initialize(node:, id:, **attributes)
5
5
  super
6
6
  @alarms = {}
7
7
  @statuses = {}
@@ -19,7 +19,12 @@ module RSMP
19
19
 
20
20
  components_by_type.each_pair do |id, component_settings|
21
21
  component_settings ||= {}
22
- @components[id] = build_component(id: id, type: type, settings: component_settings)
22
+ component = build_component(id: id, type: type, settings: component_settings)
23
+ component.update_metadata(
24
+ type: component_settings['type'] || type,
25
+ name: component_settings['name'] || id
26
+ )
27
+ @components[id] = component
23
28
  @main = @components[id] if type == 'main'
24
29
  end
25
30
  end
@@ -36,6 +41,22 @@ module RSMP
36
41
  @components[component.c_id] = component
37
42
  end
38
43
 
44
+ def natural_sort_key(value)
45
+ value.to_s.split(/(\d+)/).map do |part|
46
+ part.match?(/\A\d+\z/) ? part.to_i : part
47
+ end
48
+ end
49
+
50
+ def component_list
51
+ @components.values.sort_by { |component| natural_sort_key(component.c_id) }.map do |component|
52
+ {
53
+ 'id' => component.c_id,
54
+ 'type' => component.component_type || 'component',
55
+ 'name' => component.name || component.c_id
56
+ }
57
+ end
58
+ end
59
+
39
60
  def infer_component_type(component_id)
40
61
  raise UnknownComponent, "Component #{component_id} mising and cannot infer type"
41
62
  end
@@ -112,6 +112,7 @@ module RSMP
112
112
  'version' => meta['version'],
113
113
  'allOf' => root_type_rules
114
114
  }
115
+ json['prefix'] = meta['prefix'] if meta['prefix']
115
116
  out['rsmp.json'] = output_json json
116
117
  end
117
118
 
@@ -7,7 +7,7 @@ module RSMP
7
7
  def self.build_value(item)
8
8
  out = {}
9
9
  out['description'] = item['description'] if item['description']
10
- if item['type'] =~ /_list$/
10
+ if item['type'] =~ /_list(_as_string)?$/
11
11
  handle_string_list item, out
12
12
  else
13
13
  handle_types item, out
@@ -59,11 +59,13 @@ module RSMP
59
59
  # convert a yaml item with list: true to json schema
60
60
  def self.handle_string_list(item, out)
61
61
  case item['type']
62
- when 'boolean_list'
62
+ when 'boolean_list', 'boolean_list_as_string'
63
63
  out['$ref'] = '../defs/definitions.json#/boolean_list'
64
- when 'integer_list'
64
+ when 'integer_list', 'integer_list_as_string'
65
65
  out['$ref'] = '../defs/definitions.json#/integer_list'
66
- when 'string_list'
66
+ when 'number_list', 'number_list_as_string'
67
+ out['$ref'] = '../defs/definitions.json#/number_list'
68
+ when 'string_list', 'string_list_as_string'
67
69
  out['$ref'] = '../defs/definitions.json#/string_list'
68
70
  else
69
71
  raise "Error: List of #{item['type']} is not supported: #{item.inspect}"
@@ -26,8 +26,11 @@ module RSMP
26
26
  JSON.generate(item, JSON_OPTIONS)
27
27
  end
28
28
 
29
- # Default path to definitions.json bundled with the gem's core schemas
30
- DEFINITIONS_SOURCE = File.expand_path('../../../../schemas/core/3.1.2/definitions.json', __dir__)
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__)
33
+ end
31
34
 
32
35
  # generate the json schema from a string containing yaml
33
36
  def self.generate(sxl)
@@ -50,7 +53,8 @@ module RSMP
50
53
  # Copy definitions.json so each version folder is self-contained
51
54
  defs_dest = File.join(folder, 'defs', 'definitions.json')
52
55
  FileUtils.mkdir_p File.dirname(defs_dest)
53
- FileUtils.cp DEFINITIONS_SOURCE, defs_dest if File.exist?(DEFINITIONS_SOURCE)
56
+ source = definitions_source
57
+ FileUtils.cp source, defs_dest if File.exist?(source)
54
58
  end
55
59
  end
56
60
  end
@@ -3,8 +3,8 @@ class Hash
3
3
  def deep_merge(other_hash)
4
4
  return self unless other_hash
5
5
 
6
- merge(other_hash) do |_key, old, fresh|
7
- if old.is_a?(Hash) && fresh.is_a?(Hash)
6
+ merge(other_hash) do |key, old, fresh|
7
+ if key != 'sxls' && old.is_a?(Hash) && fresh.is_a?(Hash)
8
8
  old.deep_merge(fresh)
9
9
  else
10
10
  fresh
data/lib/rsmp/message.rb CHANGED
@@ -30,6 +30,7 @@ module RSMP
30
30
  'MessageAck' => MessageAck,
31
31
  'MessageNotAck' => MessageNotAck,
32
32
  'Version' => Version,
33
+ 'ComponentList' => ComponentList,
33
34
  'AggregatedStatus' => AggregatedStatus,
34
35
  'AggregatedStatusRequest' => AggregatedStatusRequest,
35
36
  'Watchdog' => Watchdog,
@@ -216,6 +217,37 @@ module RSMP
216
217
  def versions
217
218
  attribute('RSMP').map { |item| item['vers'] }
218
219
  end
220
+
221
+ def step
222
+ @attributes['step']
223
+ end
224
+
225
+ def request?
226
+ step == 'Request'
227
+ end
228
+
229
+ def response?
230
+ step == 'Response'
231
+ end
232
+
233
+ def sxls
234
+ (@attributes['SXLS'] || []).map do |item|
235
+ item.transform_keys(&:to_s)
236
+ end
237
+ end
238
+
239
+ def site_ids
240
+ attribute('siteId').map { |item| item['sId'] }
241
+ end
242
+ end
243
+
244
+ # ComponentList message, lists site components and their component types.
245
+ class ComponentList < Message
246
+ def initialize(attributes = {})
247
+ super({
248
+ 'type' => 'ComponentList'
249
+ }.merge attributes)
250
+ end
219
251
  end
220
252
 
221
253
  # Unknown message type wrapper.
@@ -19,8 +19,16 @@ module RSMP
19
19
  build_proxies
20
20
  end
21
21
 
22
+ def sxls
23
+ @site_settings['sxls']
24
+ end
25
+
26
+ def primary_sxl
27
+ sxls.first
28
+ end
29
+
22
30
  def sxl_version
23
- @site_settings['sxl_version']
31
+ primary_sxl && primary_sxl['version']
24
32
  end
25
33
 
26
34
  def site_id
@@ -30,18 +38,34 @@ module RSMP
30
38
  def handle_site_settings(options = {})
31
39
  options_class = self.class.options_class
32
40
  settings = options[:site_settings] || {}
41
+ settings = denormalize_sxls(settings)
33
42
  @site_options = options_class.new(settings)
34
43
  @site_settings = @site_options.to_h
35
44
 
36
- check_sxl_version
45
+ check_sxls
37
46
  check_core_versions
38
47
  setup_components @site_settings['components']
39
48
  end
40
49
 
41
- def check_sxl_version
42
- sxl = @site_settings['sxl']
43
- version = @site_settings['sxl_version'].to_s
44
- RSMP::Schema.find_schema! sxl, version, lenient: true
50
+ def denormalize_sxls(settings)
51
+ sxls = settings['sxls']
52
+ return settings unless sxls.is_a?(Array)
53
+
54
+ settings.merge(
55
+ 'sxls' => sxls.to_h { |sxl| [sxl['name'], sxl['version']] }
56
+ )
57
+ end
58
+
59
+ def check_sxls
60
+ raise RSMP::ConfigurationError, 'No SXLs specified' unless sxls
61
+
62
+ sxls.each do |sxl|
63
+ name = sxl['name']
64
+ version = sxl['version'].to_s
65
+ raise RSMP::ConfigurationError, 'SXL name cannot be core' if name.to_s == 'core'
66
+
67
+ RSMP::Schema.find_schema! name, version, lenient: true
68
+ end
45
69
  end
46
70
 
47
71
  def check_core_versions
@@ -60,7 +84,7 @@ module RSMP
60
84
 
61
85
  def log_site_starting
62
86
  log "Starting #{site_type_name} #{@site_settings['site_id']}", level: :info, timestamp: @clock.now
63
- sxl = "Using #{@site_settings['sxl']} sxl #{@site_settings['sxl_version']}"
87
+ sxl = "Using SXLs #{sxls.map { |item| "#{item['name']} #{item['version']}" }.join(', ')}"
64
88
  version = @site_settings['core_version']
65
89
  core = if version
66
90
  "accepting only core version #{version}"
@@ -113,7 +137,7 @@ module RSMP
113
137
 
114
138
  def send_alarm(alarm)
115
139
  @proxies.each do |proxy|
116
- proxy.send_message alarm if proxy.ready?
140
+ proxy.send_message alarm if proxy.ready? && proxy.receive_alarms?
117
141
  end
118
142
  end
119
143
 
@@ -158,10 +182,10 @@ module RSMP
158
182
  def build_component(id:, type:, settings:)
159
183
  settings ||= {}
160
184
  if type == 'main'
161
- Component.new id: id, node: self, grouped: true,
185
+ Component.new id: id, node: self, type: type, name: settings['name'], grouped: true,
162
186
  ntsoid: settings['ntsOId'], xnid: settings['xNId']
163
187
  else
164
- Component.new id: id, node: self, grouped: false
188
+ Component.new id: id, node: self, type: type, name: settings['name'], grouped: false
165
189
  end
166
190
  end
167
191
  end
@@ -4,22 +4,49 @@ module RSMP
4
4
  # Handles supervisor configuration and site settings
5
5
  module Configuration
6
6
  def handle_supervisor_settings(supervisor_settings)
7
+ supervisor_settings = denormalize_supervisor_sxls(supervisor_settings || {})
7
8
  options = RSMP::Supervisor::Options.new(supervisor_settings || {})
8
9
  @supervisor_settings = options.to_h
9
10
  @core_version = @supervisor_settings.dig('default', 'core_version')
10
- check_site_sxl_types
11
+ check_site_sxls
11
12
  end
12
13
 
13
- def check_site_sxl_types
14
+ def denormalize_supervisor_sxls(settings)
15
+ settings = settings.merge('default' => denormalize_site_sxls(settings['default'])) if settings['default']
16
+ return settings unless settings['sites']
17
+
18
+ settings.merge(
19
+ 'sites' => settings['sites'].transform_values { |site_settings| denormalize_site_sxls(site_settings) }
20
+ )
21
+ end
22
+
23
+ def denormalize_site_sxls(settings)
24
+ sxls = settings['sxls']
25
+ return settings unless sxls.is_a?(Array)
26
+
27
+ settings.merge(
28
+ 'sxls' => sxls.to_h { |sxl| [sxl['name'], sxl['version']] }
29
+ )
30
+ end
31
+
32
+ def check_site_sxls
14
33
  sites = @supervisor_settings['sites'].clone || {}
15
34
  sites['default'] = @supervisor_settings['default']
16
35
  sites.each do |site_id, settings|
17
36
  raise RSMP::ConfigurationError, "Configuration for site '#{site_id}' is empty" unless settings
18
37
 
19
- sxl = settings['sxl']
20
- raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': No SXL specified" unless sxl
38
+ sxls = settings['sxls']
39
+ raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': No SXLs specified" unless sxls
40
+
41
+ sxls.each do |sxl|
42
+ name = sxl['name']
43
+ if name.to_s == 'core'
44
+ raise RSMP::ConfigurationError,
45
+ "Configuration error for site '#{site_id}': SXL name cannot be core"
46
+ end
21
47
 
22
- RSMP::Schema.find_schemas! sxl if sxl
48
+ RSMP::Schema.find_schema! name, sxl['version'], lenient: true
49
+ end
23
50
  rescue RSMP::Schema::UnknownSchemaError => e
24
51
  raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': #{e}"
25
52
  end
@@ -128,8 +128,6 @@ module RSMP
128
128
  stop if @supervisor_settings['one_shot']
129
129
  end
130
130
 
131
- # Proxy type is now derived from `site_settings['sxl']` in Supervisor#build_proxy.
132
-
133
131
  def reject_connection(_socket, info)
134
132
  log 'Site rejected', ip: info[:ip], level: :info
135
133
  end
@@ -59,13 +59,6 @@ module RSMP
59
59
  end
60
60
 
61
61
  def build_proxy(settings)
62
- # Determine proxy type from site settings (SXL). Fall back to supervisor
63
- # default settings when site-specific settings are not present.
64
- site_settings = settings[:site_settings] || @supervisor_settings['default']
65
- sxl_type = site_settings && site_settings['sxl']
66
-
67
- return RSMP::TLC::TrafficControllerProxy.new(settings) if sxl_type == 'tlc'
68
-
69
62
  SiteProxy.new settings
70
63
  end
71
64