rsmp 0.37.0 → 0.38.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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/devcontainer.json +22 -0
  3. data/.github/workflows/rubocop.yaml +17 -0
  4. data/.gitignore +5 -6
  5. data/.rubocop.yml +80 -0
  6. data/Gemfile +13 -1
  7. data/Gemfile.lock +34 -1
  8. data/Rakefile +3 -3
  9. data/lib/rsmp/cli.rb +147 -124
  10. data/lib/rsmp/collect/ack_collector.rb +8 -7
  11. data/lib/rsmp/collect/aggregated_status_collector.rb +4 -4
  12. data/lib/rsmp/collect/alarm_collector.rb +31 -23
  13. data/lib/rsmp/collect/alarm_matcher.rb +3 -3
  14. data/lib/rsmp/collect/collector/logging.rb +17 -0
  15. data/lib/rsmp/collect/collector/reporting.rb +44 -0
  16. data/lib/rsmp/collect/collector/status.rb +34 -0
  17. data/lib/rsmp/collect/collector.rb +69 -150
  18. data/lib/rsmp/collect/command_matcher.rb +19 -6
  19. data/lib/rsmp/collect/command_response_collector.rb +7 -7
  20. data/lib/rsmp/collect/distributor.rb +14 -11
  21. data/lib/rsmp/collect/filter.rb +31 -15
  22. data/lib/rsmp/collect/matcher.rb +7 -11
  23. data/lib/rsmp/collect/queue.rb +4 -4
  24. data/lib/rsmp/collect/receiver.rb +10 -12
  25. data/lib/rsmp/collect/state_collector.rb +116 -77
  26. data/lib/rsmp/collect/status_collector.rb +6 -6
  27. data/lib/rsmp/collect/status_matcher.rb +17 -7
  28. data/lib/rsmp/{alarm_state.rb → component/alarm_state.rb} +76 -37
  29. data/lib/rsmp/{component.rb → component/component.rb} +15 -15
  30. data/lib/rsmp/component/component_base.rb +89 -0
  31. data/lib/rsmp/component/component_proxy.rb +75 -0
  32. data/lib/rsmp/component/components.rb +63 -0
  33. data/lib/rsmp/convert/export/json_schema.rb +116 -110
  34. data/lib/rsmp/convert/import/yaml.rb +21 -18
  35. data/lib/rsmp/{rsmp.rb → helpers/clock.rb} +5 -6
  36. data/lib/rsmp/{deep_merge.rb → helpers/deep_merge.rb} +2 -1
  37. data/lib/rsmp/helpers/error.rb +71 -0
  38. data/lib/rsmp/{inspect.rb → helpers/inspect.rb} +6 -10
  39. data/lib/rsmp/log/archive.rb +98 -0
  40. data/lib/rsmp/log/colorization.rb +41 -0
  41. data/lib/rsmp/log/filtering.rb +54 -0
  42. data/lib/rsmp/log/logger.rb +206 -0
  43. data/lib/rsmp/{logging.rb → log/logging.rb} +5 -7
  44. data/lib/rsmp/message.rb +159 -148
  45. data/lib/rsmp/{node.rb → node/node.rb} +19 -17
  46. data/lib/rsmp/{protocol.rb → node/protocol.rb} +5 -3
  47. data/lib/rsmp/node/site/site.rb +195 -0
  48. data/lib/rsmp/node/supervisor/modules/configuration.rb +59 -0
  49. data/lib/rsmp/node/supervisor/modules/connection.rb +140 -0
  50. data/lib/rsmp/node/supervisor/modules/sites.rb +64 -0
  51. data/lib/rsmp/node/supervisor/supervisor.rb +72 -0
  52. data/lib/rsmp/{task.rb → node/task.rb} +12 -14
  53. data/lib/rsmp/proxy/modules/acknowledgements.rb +144 -0
  54. data/lib/rsmp/proxy/modules/receive.rb +119 -0
  55. data/lib/rsmp/proxy/modules/send.rb +76 -0
  56. data/lib/rsmp/proxy/modules/state.rb +25 -0
  57. data/lib/rsmp/proxy/modules/tasks.rb +105 -0
  58. data/lib/rsmp/proxy/modules/versions.rb +69 -0
  59. data/lib/rsmp/proxy/modules/watchdogs.rb +66 -0
  60. data/lib/rsmp/proxy/proxy.rb +199 -0
  61. data/lib/rsmp/proxy/site/modules/aggregated_status.rb +52 -0
  62. data/lib/rsmp/proxy/site/modules/alarms.rb +27 -0
  63. data/lib/rsmp/proxy/site/modules/commands.rb +31 -0
  64. data/lib/rsmp/proxy/site/modules/status.rb +110 -0
  65. data/lib/rsmp/proxy/site/site_proxy.rb +205 -0
  66. data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +47 -0
  67. data/lib/rsmp/proxy/supervisor/modules/alarms.rb +73 -0
  68. data/lib/rsmp/proxy/supervisor/modules/commands.rb +53 -0
  69. data/lib/rsmp/proxy/supervisor/modules/status.rb +204 -0
  70. data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +178 -0
  71. data/lib/rsmp/tlc/detector_logic.rb +18 -34
  72. data/lib/rsmp/tlc/input_states.rb +126 -0
  73. data/lib/rsmp/tlc/modules/detector_logics.rb +50 -0
  74. data/lib/rsmp/tlc/modules/display.rb +78 -0
  75. data/lib/rsmp/tlc/modules/helpers.rb +41 -0
  76. data/lib/rsmp/tlc/modules/inputs.rb +173 -0
  77. data/lib/rsmp/tlc/modules/modes.rb +253 -0
  78. data/lib/rsmp/tlc/modules/outputs.rb +30 -0
  79. data/lib/rsmp/tlc/modules/plans.rb +218 -0
  80. data/lib/rsmp/tlc/modules/signal_groups.rb +109 -0
  81. data/lib/rsmp/tlc/modules/startup_sequence.rb +22 -0
  82. data/lib/rsmp/tlc/modules/system.rb +140 -0
  83. data/lib/rsmp/tlc/modules/traffic_data.rb +49 -0
  84. data/lib/rsmp/tlc/signal_group.rb +37 -41
  85. data/lib/rsmp/tlc/signal_plan.rb +14 -11
  86. data/lib/rsmp/tlc/signal_priority.rb +39 -35
  87. data/lib/rsmp/tlc/startup_sequence.rb +59 -0
  88. data/lib/rsmp/tlc/traffic_controller.rb +38 -1010
  89. data/lib/rsmp/tlc/traffic_controller_site.rb +58 -57
  90. data/lib/rsmp/version.rb +1 -1
  91. data/lib/rsmp.rb +82 -48
  92. data/rsmp.gemspec +24 -31
  93. metadata +79 -139
  94. data/lib/rsmp/archive.rb +0 -76
  95. data/lib/rsmp/collect/message_matchers.rb +0 -0
  96. data/lib/rsmp/component_base.rb +0 -87
  97. data/lib/rsmp/component_proxy.rb +0 -57
  98. data/lib/rsmp/components.rb +0 -65
  99. data/lib/rsmp/error.rb +0 -71
  100. data/lib/rsmp/logger.rb +0 -216
  101. data/lib/rsmp/proxy.rb +0 -693
  102. data/lib/rsmp/site.rb +0 -188
  103. data/lib/rsmp/site_proxy.rb +0 -389
  104. data/lib/rsmp/supervisor.rb +0 -302
  105. data/lib/rsmp/supervisor_proxy.rb +0 -510
  106. data/lib/rsmp/tlc/inputs.rb +0 -134
@@ -1,302 +0,0 @@
1
- # RSMP supervisor (server)
2
- # The supervisor waits for sites to connect.
3
- # Connections to sites are handles via site proxies.
4
-
5
- module RSMP
6
- class Supervisor < Node
7
- attr_reader :core_version, :site_id, :supervisor_settings, :proxies, :logger, :ready_condition
8
-
9
- attr_accessor :site_id_condition
10
-
11
- def initialize options={}
12
- handle_supervisor_settings( options[:supervisor_settings] || {} )
13
- super options
14
- @proxies = []
15
- @ready_condition = Async::Notification.new
16
- @site_id_condition = Async::Notification.new
17
- end
18
-
19
- def site_id
20
- @supervisor_settings['site_id']
21
- end
22
-
23
- def handle_supervisor_settings supervisor_settings
24
- defaults = {
25
- 'port' => 12111,
26
- 'ips' => 'all',
27
- 'guest' => {
28
- 'sxl' => 'tlc',
29
- 'intervals' => {
30
- 'timer' => 1,
31
- 'watchdog' => 1
32
- },
33
- 'timeouts' => {
34
- 'watchdog' => 2,
35
- 'acknowledgement' => 2
36
- }
37
- }
38
- }
39
-
40
- # merge options into defaults
41
- @supervisor_settings = defaults.deep_merge(supervisor_settings)
42
- @core_version = @supervisor_settings["guest"]["core_version"]
43
- check_site_sxl_types
44
- end
45
-
46
- def check_site_sxl_types
47
- sites = @supervisor_settings['sites'].clone || {}
48
- sites['guest'] = @supervisor_settings['guest']
49
- sites.each do |site_id,settings|
50
- unless settings
51
- raise RSMP::ConfigurationError.new("Configuration for site '#{site_id}' is empty")
52
- end
53
- sxl = settings['sxl']
54
- unless sxl
55
- raise RSMP::ConfigurationError.new("Configuration error for site '#{site_id}': No SXL specified")
56
- end
57
- RSMP::Schema.find_schemas! sxl if sxl
58
- rescue RSMP::Schema::UnknownSchemaError => e
59
- raise RSMP::ConfigurationError.new("Configuration error for site '#{site_id}': #{e}")
60
- end
61
- end
62
-
63
- # listen for connections
64
- def run
65
- log "Starting supervisor on port #{@supervisor_settings["port"]}",
66
- level: :info,
67
- timestamp: @clock.now
68
-
69
- @endpoint = IO::Endpoint.tcp('0.0.0.0', @supervisor_settings["port"])
70
- @accept_task = Async::Task.current.async do |task|
71
- task.annotate "supervisor accept loop"
72
- @endpoint.accept() do |socket| # creates fibers
73
- handle_connection(socket)
74
- rescue StandardError => e
75
- distribute_error e, level: :internal
76
- end
77
- rescue Async::Stop # will happen at shutdown
78
- rescue StandardError => e
79
- distribute_error e, level: :internal
80
- end
81
-
82
- @ready_condition.signal
83
- @accept_task.wait
84
- rescue StandardError => e
85
- distribute_error e, level: :internal
86
- end
87
-
88
- # stop
89
- def stop
90
- log "Stopping supervisor #{@supervisor_settings["site_id"]}", level: :info
91
-
92
- @accept_task.stop if @accept_task
93
- @accept_task = nil
94
-
95
- @endpoint = nil
96
- super
97
- end
98
-
99
- # handle an incoming connction by either accepting of rejecting it
100
- def handle_connection socket
101
- remote_port = socket.remote_address.ip_port
102
- remote_hostname = socket.remote_address.ip_address
103
- remote_ip = socket.remote_address.ip_address
104
-
105
- info = {ip:remote_ip, port:remote_port, hostname:remote_hostname, now:Clock.now}
106
- if accept? socket, info
107
- accept_connection socket, info
108
- else
109
- reject_connection socket, info
110
- end
111
- rescue ConnectionError, HandshakeError => e
112
- log "Rejected connection from #{remote_ip}:#{remote_port}, #{e.to_s}", level: :warning
113
- distribute_error e
114
- rescue StandardError => e
115
- log "Connection: #{e.to_s}", exception: e, level: :error
116
- distribute_error e, level: :internal
117
- ensure
118
- close socket, info
119
- end
120
-
121
- def accept? socket, info
122
- true
123
- end
124
-
125
- def build_proxy settings
126
- SiteProxy.new settings
127
- end
128
-
129
- def format_ip_and_port info
130
- if @logger.settings['hide_ip_and_port']
131
- '********'
132
- else
133
- "#{info[:ip]}:#{info[:port]}"
134
- end
135
- end
136
-
137
- def authorize_ip ip
138
- return if @supervisor_settings['ips'] == 'all'
139
- return if @supervisor_settings['ips'].include? ip
140
- raise ConnectionError.new('guest ip not allowed')
141
- end
142
-
143
- def check_max_sites
144
- max = @supervisor_settings['max_sites']
145
- if max
146
- if @proxies.size >= max
147
- raise ConnectionError.new("maximum of #{max} sites already connected")
148
- end
149
- end
150
- end
151
-
152
- def peek_version_message protocol
153
- json = protocol.peek_line
154
- attributes = Message.parse_attributes json
155
- Message.build attributes, json
156
- end
157
-
158
- # accept an incoming connecting by creating and starting a proxy
159
- def accept_connection socket, info
160
- log "Site connected from #{format_ip_and_port(info)}",
161
- ip: info[:ip],
162
- port: info[:port],
163
- level: :info,
164
- timestamp: Clock.now
165
-
166
- authorize_ip info[:ip]
167
-
168
- stream = IO::Stream::Buffered.new(socket)
169
- protocol = RSMP::Protocol.new stream
170
-
171
- settings = {
172
- supervisor: self,
173
- ip: info[:ip],
174
- port: info[:port],
175
- task: @task,
176
- collect: @collect,
177
- socket: socket,
178
- stream: stream,
179
- protocol: protocol,
180
- info: info,
181
- logger: @logger,
182
- archive: @archive
183
- }
184
-
185
- version_message = peek_version_message protocol
186
- id = version_message.attribute('siteId').first['sId']
187
-
188
- proxy = find_site id
189
- if proxy
190
- if proxy.connected?
191
- raise ConnectionError.new("Site #{id} alredy connected from port #{proxy.port}")
192
- else
193
- proxy.revive settings
194
- end
195
- else
196
- check_max_sites
197
- proxy = build_proxy settings.merge(site_id:id) # keep the id learned by peeking above
198
- @proxies.push proxy
199
- end
200
-
201
- proxy.setup_site_settings
202
- proxy.check_core_version version_message
203
- log "Validating using core version #{proxy.core_version}", level: :debug
204
-
205
- proxy.start # will run until the site disconnects
206
- proxy.wait
207
- ensure
208
- site_ids_changed
209
- stop if @supervisor_settings['one_shot']
210
- end
211
-
212
- def site_ids_changed
213
- @site_id_condition.signal
214
- end
215
-
216
- def reject_connection socket, info
217
- log "Site rejected", ip: info[:ip], level: :info
218
- end
219
-
220
- def close socket, info
221
- if info
222
- log "Connection to #{format_ip_and_port(info)} closed", ip: info[:ip], level: :info, timestamp: Clock.now
223
- else
224
- log "Connection closed", level: :info, timestamp: Clock.now
225
- end
226
-
227
- socket.close
228
- end
229
-
230
- def site_connected? site_id
231
- return find_site(site_id) != nil
232
- end
233
-
234
- def find_site_from_ip_port ip, port
235
- @proxies.each do |site|
236
- return site if site.ip == ip && site.port == port
237
- end
238
- nil
239
- end
240
-
241
- def find_site site_id
242
- @proxies.each do |site|
243
- return site if site_id == :any || site.site_id == site_id
244
- end
245
- nil
246
- end
247
-
248
- def wait_for_site site_id, timeout:
249
- site = find_site site_id
250
- return site if site
251
- wait_for_condition(@site_id_condition,timeout:timeout) do
252
- find_site site_id
253
- end
254
-
255
- rescue Async::TimeoutError
256
- if site_id == :any
257
- str = "No site connected"
258
- else
259
- str = "Site '#{site_id}' did not connect"
260
- end
261
- raise RSMP::TimeoutError.new "#{str} within #{timeout}s"
262
- end
263
-
264
- def wait_for_site_disconnect site_id, timeout:
265
- wait_for_condition(@site_id_condition,timeout:timeout) { true unless find_site site_id }
266
- rescue Async::TimeoutError
267
- raise RSMP::TimeoutError.new "Site '#{site_id}' did not disconnect within #{timeout}s"
268
- end
269
-
270
- def check_site_id site_id
271
- #check_site_already_connected site_id
272
- return site_id_to_site_setting site_id
273
- end
274
-
275
- def check_site_already_connected site_id
276
- site = find_site(site_id)
277
- raise HandshakeError.new "Site '#{site_id}' already connected" if site != nil && site != self
278
- end
279
-
280
- def site_id_to_site_setting site_id
281
- return {} unless @supervisor_settings['sites']
282
- @supervisor_settings['sites'].each_pair do |id,settings|
283
- if id == 'guest' || id == site_id
284
- return settings
285
- end
286
- end
287
- raise HandshakeError.new "site id #{site_id} unknown"
288
- end
289
-
290
- def ip_to_site_settings ip
291
- @supervisor_settings['sites'][ip] || @supervisor_settings['sites']['guest']
292
- end
293
-
294
- def aggregated_status_changed site_proxy, component
295
- end
296
-
297
- def self.build_id_from_ip_port ip, port
298
- Digest::MD5.hexdigest("#{ip}:#{port}")[0..8]
299
- end
300
-
301
- end
302
- end