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
@@ -0,0 +1,253 @@
1
+ module RSMP
2
+ module TLC
3
+ module Modules
4
+ # Operating modes and functional positions for traffic controllers
5
+ # Handles mode switching (NormalControl/YellowFlash/Dark) and control modes
6
+ module Modes
7
+ def reset_modes
8
+ @function_position = 'NormalControl'
9
+ @function_position_source = 'startup'
10
+ @previous_functional_position = nil
11
+ @functional_position_timeout = nil
12
+
13
+ @booting = false
14
+ @is_starting = false
15
+ @control_mode = 'control'
16
+ @manual_control = false
17
+ @manual_control_source = 'startup'
18
+ @fixed_time_control = false
19
+ @fixed_time_control_source = 'startup'
20
+ @isolated_control = false
21
+ @isolated_control_source = 'startup'
22
+ @all_red = false
23
+ @all_red_source = 'startup'
24
+ @police_key = 0
25
+ end
26
+
27
+ def check_functional_position_timeout
28
+ return unless @functional_position_timeout
29
+ return unless clock.now >= @functional_position_timeout
30
+
31
+ switch_functional_position @previous_functional_position, reverting: true, source: 'calendar_clock'
32
+ @functional_position_timeout = nil
33
+ @previous_functional_position = nil
34
+ end
35
+
36
+ def dark?
37
+ @function_position == 'Dark'
38
+ end
39
+
40
+ def yellow_flash?
41
+ @function_position == 'YellowFlash'
42
+ end
43
+
44
+ def normal_control?
45
+ @function_position == 'NormalControl'
46
+ end
47
+
48
+ # M0001 - Set functional position
49
+ def handle_m0001(arg, _options = {})
50
+ @node.verify_security_code 2, arg['securityCode']
51
+
52
+ # timeout is specified in minutes, but we take 1 to mean 1s
53
+ # this is not according to the curent rsmp spec, but is done
54
+ # to speed up testing
55
+ timeout = arg['timeout'].to_i
56
+ if timeout == 1
57
+ timeout = 1
58
+ else
59
+ timeout *= 60
60
+ end
61
+
62
+ switch_functional_position arg['status'],
63
+ timeout: timeout,
64
+ source: 'forced'
65
+ end
66
+
67
+ # M0007 - Set fixed time control
68
+ def handle_m0007(arg, _options = {})
69
+ @node.verify_security_code 2, arg['securityCode']
70
+ set_fixed_time_control arg['status'], source: 'forced'
71
+ end
72
+
73
+ # M0005 - Enable/disable emergency route
74
+ def handle_m0005(arg, _options = {})
75
+ @node.verify_security_code 2, arg['securityCode']
76
+ route = arg['emergencyroute'].to_i
77
+ enable = (arg['status'] == 'True')
78
+ @last_emergency_route = route
79
+
80
+ if enable
81
+ if @emergency_routes.add? route
82
+ log "Enabling emergency route #{route}", level: :info
83
+ else
84
+ log "Emergency route #{route} already enabled", level: :info
85
+ end
86
+ elsif @emergency_routes.delete? route
87
+ log "Disabling emergency route #{route}", level: :info
88
+ else
89
+ log "Emergency route #{route} already disabled", level: :info
90
+ end
91
+ end
92
+
93
+ # S0007 - Intersection status
94
+ def handle_s0007(_status_code, status_name = nil, _options = {})
95
+ case status_name
96
+ when 'intersection'
97
+ TrafficControllerSite.make_status @intersection
98
+ when 'status'
99
+ TrafficControllerSite.make_status @function_position != 'Dark'
100
+ when 'source'
101
+ TrafficControllerSite.make_status @function_position_source
102
+ end
103
+ end
104
+
105
+ # S0008 - Manual control status
106
+ def handle_s0008(_status_code, status_name = nil, _options = {})
107
+ case status_name
108
+ when 'intersection'
109
+ TrafficControllerSite.make_status @intersection
110
+ when 'status'
111
+ TrafficControllerSite.make_status @manual_control
112
+ when 'source'
113
+ TrafficControllerSite.make_status @manual_control_source
114
+ end
115
+ end
116
+
117
+ # S0009 - Fixed time control status
118
+ def handle_s0009(_status_code, status_name = nil, _options = {})
119
+ case status_name
120
+ when 'intersection'
121
+ TrafficControllerSite.make_status @intersection
122
+ when 'status'
123
+ TrafficControllerSite.make_status @fixed_time_control
124
+ when 'source'
125
+ TrafficControllerSite.make_status @fixed_time_control_source
126
+ end
127
+ end
128
+
129
+ # S0010 - Isolated control status
130
+ def handle_s0010(_status_code, status_name = nil, _options = {})
131
+ case status_name
132
+ when 'intersection'
133
+ TrafficControllerSite.make_status @intersection
134
+ when 'status'
135
+ TrafficControllerSite.make_status @isolated_control
136
+ when 'source'
137
+ TrafficControllerSite.make_status @isolated_control_source
138
+ end
139
+ end
140
+
141
+ # S0011 - Yellow flash status
142
+ def handle_s0011(_status_code, status_name = nil, _options = {})
143
+ case status_name
144
+ when 'intersection'
145
+ TrafficControllerSite.make_status @intersection
146
+ when 'status'
147
+ TrafficControllerSite.make_status TrafficControllerSite.to_rmsp_bool(@function_position == 'YellowFlash')
148
+ when 'source'
149
+ TrafficControllerSite.make_status @function_position_source
150
+ end
151
+ end
152
+
153
+ # S0012 - All red status
154
+ def handle_s0012(_status_code, status_name = nil, _options = {})
155
+ case status_name
156
+ when 'intersection'
157
+ TrafficControllerSite.make_status @intersection
158
+ when 'status'
159
+ TrafficControllerSite.make_status @all_red
160
+ when 'source'
161
+ TrafficControllerSite.make_status @all_red_source
162
+ end
163
+ end
164
+
165
+ # S0013 - Police key status
166
+ def handle_s0013(_status_code, status_name = nil, _options = {})
167
+ case status_name
168
+ when 'intersection'
169
+ TrafficControllerSite.make_status @intersection
170
+ when 'status'
171
+ TrafficControllerSite.make_status @police_key
172
+ end
173
+ end
174
+
175
+ # S0020 - Control mode
176
+ def handle_s0020(_status_code, status_name = nil, _options = {})
177
+ case status_name
178
+ when 'intersection'
179
+ TrafficControllerSite.make_status @intersection
180
+ when 'controlmode'
181
+ TrafficControllerSite.make_status @control_mode
182
+ end
183
+ end
184
+
185
+ # S0032 - Coordination status
186
+ def handle_s0032(_status_code, status_name = nil, _options = {})
187
+ case status_name
188
+ when 'intersection'
189
+ TrafficControllerSite.make_status @intersection
190
+ when 'status'
191
+ TrafficControllerSite.make_status 'local'
192
+ when 'source'
193
+ TrafficControllerSite.make_status @intersection_source
194
+ end
195
+ end
196
+
197
+ # S0006 - Emergency route status (deprecated, use S0035)
198
+ def handle_s0006(_status_code, status_name = nil, options = {})
199
+ if Proxy.version_meets_requirement? options[:sxl_version],
200
+ '>=1.2.0'
201
+ log 'S0006 is depreciated, use S0035 instead.',
202
+ level: :warning
203
+ end
204
+ status = @emergency_routes.any?
205
+ case status_name
206
+ when 'status'
207
+ TrafficControllerSite.make_status status
208
+ when 'emergencystage'
209
+ TrafficControllerSite.make_status status ? @last_emergency_route : 0
210
+ end
211
+ end
212
+
213
+ # S0035 - Emergency routes (replaces S0006)
214
+ def handle_s0035(_status_code, status_name = nil, _options = {})
215
+ case status_name
216
+ when 'emergencyroutes'
217
+ list = @emergency_routes.sort.map { |route| { 'id' => route.to_s } }
218
+ TrafficControllerSite.make_status list
219
+ end
220
+ end
221
+
222
+ def set_fixed_time_control(status, source:)
223
+ @fixed_time_control = status
224
+ @fixed_time_control_source = source
225
+ end
226
+
227
+ def switch_functional_position(mode, source:, timeout: nil, reverting: false)
228
+ unless %w[NormalControl YellowFlash Dark].include? mode
229
+ raise RSMP::MessageRejected,
230
+ "Invalid functional position #{mode.inspect}, must be NormalControl, YellowFlash or Dark"
231
+ end
232
+
233
+ if reverting
234
+ log "Reverting to functional position #{mode} after timeout", level: :info
235
+ elsif timeout&.positive?
236
+ log "Switching to functional position #{mode} with timeout #{(timeout / 60).round(1)}min", level: :info
237
+ @previous_functional_position = @function_position
238
+ now = clock.now
239
+ @functional_position_timeout = now + timeout
240
+ else
241
+ log "Switching to functional position #{mode}", level: :info
242
+ end
243
+ initiate_startup_sequence if (mode == 'NormalControl') && (@function_position != 'NormalControl')
244
+ @function_position = mode
245
+ @function_position_source = source
246
+ # Update signal group states immediately to reflect new functional position
247
+ @signal_groups.each(&:timer)
248
+ mode
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,30 @@
1
+ module RSMP
2
+ module TLC
3
+ module Modules
4
+ # Output management for traffic controller
5
+ # Handles output status queries and forcing
6
+ module Outputs
7
+ # M0020 - Force output
8
+ def handle_m0020(arg, _options = {})
9
+ @node.verify_security_code 2, arg['securityCode']
10
+ end
11
+
12
+ # S0004 - Output status
13
+ def handle_s0004(_status_code, status_name = nil, _options = {})
14
+ case status_name
15
+ when 'outputstatus', 'extendedoutputstatus'
16
+ TrafficControllerSite.make_status 0
17
+ end
18
+ end
19
+
20
+ # S0030 - Forced output status
21
+ def handle_s0030(_status_code, status_name = nil, _options = {})
22
+ case status_name
23
+ when 'status'
24
+ TrafficControllerSite.make_status ''
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,218 @@
1
+ module RSMP
2
+ module TLC
3
+ module Modules
4
+ # Time plans, traffic situations, and scheduling management
5
+ # Handles time plan selection, dynamic bands, schedules, and cycle times
6
+ module Plans
7
+ def current_plan
8
+ # TODO: plan 0 should means use time table
9
+ return unless @plans
10
+
11
+ @plans[plan] || @plans.values.first
12
+ end
13
+
14
+ # M0002 - Set current time plan
15
+ def handle_m0002(arg, _options = {})
16
+ @node.verify_security_code 2, arg['securityCode']
17
+ if TrafficControllerSite.from_rsmp_bool?(arg['status'])
18
+ switch_plan arg['timeplan'], source: 'forced'
19
+ else
20
+ switch_plan 0, source: 'startup' # TODO: use clock/calender
21
+ end
22
+ end
23
+
24
+ # M0003 - Set traffic situation
25
+ def handle_m0003(arg, _options = {})
26
+ @node.verify_security_code 2, arg['securityCode']
27
+ switch_traffic_situation arg['traficsituation']
28
+ end
29
+
30
+ def find_plan(plan_nr)
31
+ plan = @plans[plan_nr.to_i]
32
+ raise InvalidMessage, "unknown signal plan #{plan_nr}, known only [#{@plans.keys.join(', ')}]" unless plan
33
+
34
+ plan
35
+ end
36
+
37
+ def switch_plan(plan, source:)
38
+ plan_nr = plan.to_i
39
+ if plan_nr.zero?
40
+ log 'Switching to plan selection by time table', level: :info
41
+ else
42
+ find_plan plan_nr
43
+ log "Switching to plan #{plan_nr}", level: :info
44
+ end
45
+ @plan = plan_nr
46
+ @plan_source = source
47
+ end
48
+
49
+ def switch_traffic_situation(situation)
50
+ @traffic_situation = situation.to_i
51
+ @traffic_situation_source = 'forced'
52
+ end
53
+
54
+ # M0014 - Set dynamic bands
55
+ def handle_m0014(arg, _options = {})
56
+ @node.verify_security_code 2, arg['securityCode']
57
+ plan = find_plan arg['plan']
58
+ arg['status'].split(',').each do |item|
59
+ matched = /(\d+)-(\d+)/.match item
60
+ band = matched[1].to_i
61
+ value = matched[2].to_i
62
+ log "Set plan #{arg['plan']} dynamic band #{band} to #{value}", level: :info
63
+ plan.set_band band, value
64
+ end
65
+ end
66
+
67
+ # M0015 - Set offset time
68
+ def handle_m0015(arg, _options = {})
69
+ @node.verify_security_code 2, arg['securityCode']
70
+ end
71
+
72
+ # M0016 - Set week time table
73
+ def handle_m0016(arg, _options = {})
74
+ @node.verify_security_code 2, arg['securityCode']
75
+ end
76
+
77
+ # M0017 - Set time tables
78
+ def handle_m0017(arg, _options = {})
79
+ @node.verify_security_code 2, arg['securityCode']
80
+ arg['status'].split(',').each do |item|
81
+ elems = item.split('-')
82
+ nr = elems[0].to_i
83
+ plan = elems[1].to_i
84
+ hour = elems[2].to_i
85
+ min = elems[3].to_i
86
+ raise InvalidMessage, "time table id must be between 0 and 12, got #{nr}" if nr.negative? || nr > 12
87
+
88
+ @day_time_table[nr] = { plan: plan, hour: hour, min: min }
89
+ end
90
+ end
91
+
92
+ # M0018 - Set cycle time
93
+ def handle_m0018(arg, _options = {})
94
+ @node.verify_security_code 2, arg['securityCode']
95
+ nr = arg['plan'].to_i
96
+ cycle_time = arg['status'].to_i
97
+ plan = @plans[nr]
98
+ raise RSMP::MessageRejected, "Plan '#{nr}' not found" unless plan
99
+ raise RSMP::MessageRejected, 'Cycle time must be greater or equal to zero' if cycle_time.negative?
100
+
101
+ log "Set plan #{nr} cycle time to #{cycle_time}", level: :info
102
+ plan.cycle_time = cycle_time
103
+ end
104
+
105
+ # M0023 - Dynamic bands timeout
106
+ def handle_m0023(arg, _options = {})
107
+ @node.verify_security_code 2, arg['securityCode']
108
+ timeout = arg['status'].to_i
109
+ unless (timeout >= 0) && (timeout <= 65_535)
110
+ raise RSMP::MessageRejected,
111
+ "Timeout must be in the range 0-65535, got #{timeout}"
112
+ end
113
+
114
+ if timeout.zero?
115
+ log 'Dynamic bands timeout disabled', level: :info
116
+ else
117
+ log "Dynamic bands timeout set to #{timeout}min", level: :info
118
+ end
119
+ @dynamic_bands_timeout = timeout
120
+ end
121
+
122
+ # S0014 - Current signal program
123
+ def handle_s0014(_status_code, status_name = nil, _options = {})
124
+ case status_name
125
+ when 'status'
126
+ TrafficControllerSite.make_status @plan
127
+ when 'source'
128
+ TrafficControllerSite.make_status @plan_source
129
+ end
130
+ end
131
+
132
+ # S0015 - Current traffic situation
133
+ def handle_s0015(_status_code, status_name = nil, _options = {})
134
+ case status_name
135
+ when 'status'
136
+ TrafficControllerSite.make_status @traffic_situation
137
+ when 'source'
138
+ TrafficControllerSite.make_status @traffic_situation_source
139
+ end
140
+ end
141
+
142
+ # S0018 - Number of time plans
143
+ def handle_s0018(_status_code, status_name = nil, _options = {})
144
+ case status_name
145
+ when 'number'
146
+ TrafficControllerSite.make_status @plans.size
147
+ end
148
+ end
149
+
150
+ # S0019 - Number of traffic situations
151
+ def handle_s0019(_status_code, status_name = nil, _options = {})
152
+ case status_name
153
+ when 'number'
154
+ TrafficControllerSite.make_status @num_traffic_situations
155
+ end
156
+ end
157
+
158
+ # S0022 - List of time plans
159
+ def handle_s0022(_status_code, status_name = nil, _options = {})
160
+ case status_name
161
+ when 'status'
162
+ TrafficControllerSite.make_status @plans.keys.join(',')
163
+ end
164
+ end
165
+
166
+ # S0023 - Dynamic bands
167
+ def handle_s0023(_status_code, status_name = nil, _options = {})
168
+ case status_name
169
+ when 'status'
170
+ dynamic_bands = @plans.map { |_nr, plan| plan.dynamic_bands_string }
171
+ str = dynamic_bands.compact.join(',')
172
+ TrafficControllerSite.make_status str
173
+ end
174
+ end
175
+
176
+ # S0024 - Offset times
177
+ def handle_s0024(_status_code, status_name = nil, _options = {})
178
+ case status_name
179
+ when 'status'
180
+ TrafficControllerSite.make_status '1-0'
181
+ end
182
+ end
183
+
184
+ # S0026 - Week time table
185
+ def handle_s0026(_status_code, status_name = nil, _options = {})
186
+ case status_name
187
+ when 'status'
188
+ TrafficControllerSite.make_status '0-00'
189
+ end
190
+ end
191
+
192
+ # S0027 - Time tables
193
+ def handle_s0027(_status_code, status_name = nil, _options = {})
194
+ case status_name
195
+ when 'status'
196
+ status = @day_time_table.map do |i, item|
197
+ "#{i}-#{item[:plan]}-#{item[:hour]}-#{item[:min]}"
198
+ end.join(',')
199
+ TrafficControllerSite.make_status status
200
+ end
201
+ end
202
+
203
+ # S0028 - Cycle time
204
+ def handle_s0028(_status_code, status_name = nil, _options = {})
205
+ case status_name
206
+ when 'status'
207
+ times = @plans.map do |_nr, plan|
208
+ "#{format('%02d', plan.number)}-#{format('%02d', plan.cycle_time)}"
209
+ end.join(',')
210
+ TrafficControllerSite.make_status times
211
+ end
212
+ rescue StandardError => e
213
+ puts e
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,109 @@
1
+ module RSMP
2
+ module TLC
3
+ module Modules
4
+ # Signal groups and signal priority management
5
+ # Handles signal group status and priority requests
6
+ module SignalGroups
7
+ def add_signal_group(group)
8
+ @signal_groups << group
9
+ end
10
+
11
+ def signal_priority_changed(priority, state); end
12
+
13
+ # remove all stale priority requests
14
+ def prune_priorities
15
+ @signal_priorities.delete_if(&:prune?)
16
+ end
17
+
18
+ def priority_list
19
+ @signal_priorities.map do |priority|
20
+ {
21
+ 'r' => priority.id,
22
+ 't' => RSMP::Clock.to_s(priority.updated),
23
+ 's' => priority.state
24
+ }
25
+ end
26
+ end
27
+
28
+ # M0022 - Signal priority request
29
+ def handle_m0022(arg, _options = {})
30
+ id = arg['requestId']
31
+ type = arg['type']
32
+ priority = find_signal_priority(id)
33
+
34
+ case type
35
+ when 'new'
36
+ create_priority_request(id, priority, arg)
37
+ when 'update'
38
+ update_priority_request(id, priority)
39
+ when 'cancel'
40
+ cancel_priority_request(id, priority)
41
+ else
42
+ raise MessageRejected, "Unknown type #{type}"
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def find_signal_priority(id)
49
+ @signal_priorities.find { |priority| priority.id == id }
50
+ end
51
+
52
+ def create_priority_request(id, existing_priority, arg)
53
+ raise MessageRejected, "Priority Request #{id} already exists" if existing_priority
54
+
55
+ signal_group = node.find_component arg['signalGroupId'] if arg['signalGroupId']
56
+ @signal_priorities << SignalPriority.new(
57
+ node: self,
58
+ id: id,
59
+ level: arg['level'],
60
+ eta: arg['eta'],
61
+ vehicle_type: arg['vehicleType']
62
+ )
63
+ log "Priority request #{id} for signal group #{signal_group.c_id} received.", level: :info
64
+ end
65
+
66
+ def update_priority_request(id, priority)
67
+ raise MessageRejected, "Cannot update priority request #{id}, not found" unless priority
68
+
69
+ log "Updating Priority Request #{id}", level: :info
70
+ end
71
+
72
+ def cancel_priority_request(id, priority)
73
+ raise MessageRejected, "Cannot cancel priority request #{id}, not found" unless priority
74
+
75
+ priority.cancel
76
+ log "Priority request with id #{id} cancelled.", level: :info
77
+ end
78
+
79
+ # S0001 - Signal group status
80
+ def handle_s0001(_status_code, status_name = nil, _options = {})
81
+ case status_name
82
+ when 'signalgroupstatus'
83
+ TrafficControllerSite.make_status format_signal_group_status
84
+ when 'cyclecounter', 'basecyclecounter'
85
+ TrafficControllerSite.make_status @cycle_counter.to_s
86
+ when 'stage'
87
+ TrafficControllerSite.make_status 0.to_s
88
+ end
89
+ end
90
+
91
+ # S0017 - Number of signal groups
92
+ def handle_s0017(_status_code, status_name = nil, _options = {})
93
+ case status_name
94
+ when 'number'
95
+ TrafficControllerSite.make_status @signal_groups.size
96
+ end
97
+ end
98
+
99
+ # S0033 - Signal priority status
100
+ def handle_s0033(_status_code, status_name = nil, _options = {})
101
+ case status_name
102
+ when 'status'
103
+ TrafficControllerSite.make_status priority_list
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,22 @@
1
+ module RSMP
2
+ module TLC
3
+ module Modules
4
+ # Startup sequence management
5
+ module StartupSequence
6
+ def startup_state
7
+ @startup_sequence.current_state
8
+ end
9
+
10
+ def initiate_startup_sequence
11
+ log 'Initiating startup sequence', level: :info
12
+ reset_modes
13
+ @startup_sequence.start
14
+ end
15
+
16
+ def end_startup_sequence
17
+ @startup_sequence.stop
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end