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.
- checksums.yaml +4 -4
- data/.devcontainer/devcontainer.json +22 -0
- data/.github/workflows/rubocop.yaml +17 -0
- data/.gitignore +5 -6
- data/.rubocop.yml +80 -0
- data/Gemfile +13 -1
- data/Gemfile.lock +34 -1
- data/Rakefile +3 -3
- data/lib/rsmp/cli.rb +147 -124
- data/lib/rsmp/collect/ack_collector.rb +8 -7
- data/lib/rsmp/collect/aggregated_status_collector.rb +4 -4
- data/lib/rsmp/collect/alarm_collector.rb +31 -23
- data/lib/rsmp/collect/alarm_matcher.rb +3 -3
- data/lib/rsmp/collect/collector/logging.rb +17 -0
- data/lib/rsmp/collect/collector/reporting.rb +44 -0
- data/lib/rsmp/collect/collector/status.rb +34 -0
- data/lib/rsmp/collect/collector.rb +69 -150
- data/lib/rsmp/collect/command_matcher.rb +19 -6
- data/lib/rsmp/collect/command_response_collector.rb +7 -7
- data/lib/rsmp/collect/distributor.rb +14 -11
- data/lib/rsmp/collect/filter.rb +31 -15
- data/lib/rsmp/collect/matcher.rb +7 -11
- data/lib/rsmp/collect/queue.rb +4 -4
- data/lib/rsmp/collect/receiver.rb +10 -12
- data/lib/rsmp/collect/state_collector.rb +116 -77
- data/lib/rsmp/collect/status_collector.rb +6 -6
- data/lib/rsmp/collect/status_matcher.rb +17 -7
- data/lib/rsmp/{alarm_state.rb → component/alarm_state.rb} +76 -37
- data/lib/rsmp/{component.rb → component/component.rb} +15 -15
- data/lib/rsmp/component/component_base.rb +89 -0
- data/lib/rsmp/component/component_proxy.rb +75 -0
- data/lib/rsmp/component/components.rb +63 -0
- data/lib/rsmp/convert/export/json_schema.rb +116 -110
- data/lib/rsmp/convert/import/yaml.rb +21 -18
- data/lib/rsmp/{rsmp.rb → helpers/clock.rb} +5 -6
- data/lib/rsmp/{deep_merge.rb → helpers/deep_merge.rb} +2 -1
- data/lib/rsmp/helpers/error.rb +71 -0
- data/lib/rsmp/{inspect.rb → helpers/inspect.rb} +6 -10
- data/lib/rsmp/log/archive.rb +98 -0
- data/lib/rsmp/log/colorization.rb +41 -0
- data/lib/rsmp/log/filtering.rb +54 -0
- data/lib/rsmp/log/logger.rb +206 -0
- data/lib/rsmp/{logging.rb → log/logging.rb} +5 -7
- data/lib/rsmp/message.rb +159 -148
- data/lib/rsmp/{node.rb → node/node.rb} +19 -17
- data/lib/rsmp/{protocol.rb → node/protocol.rb} +5 -3
- data/lib/rsmp/node/site/site.rb +195 -0
- data/lib/rsmp/node/supervisor/modules/configuration.rb +59 -0
- data/lib/rsmp/node/supervisor/modules/connection.rb +140 -0
- data/lib/rsmp/node/supervisor/modules/sites.rb +64 -0
- data/lib/rsmp/node/supervisor/supervisor.rb +72 -0
- data/lib/rsmp/{task.rb → node/task.rb} +12 -14
- data/lib/rsmp/proxy/modules/acknowledgements.rb +144 -0
- data/lib/rsmp/proxy/modules/receive.rb +119 -0
- data/lib/rsmp/proxy/modules/send.rb +76 -0
- data/lib/rsmp/proxy/modules/state.rb +25 -0
- data/lib/rsmp/proxy/modules/tasks.rb +105 -0
- data/lib/rsmp/proxy/modules/versions.rb +69 -0
- data/lib/rsmp/proxy/modules/watchdogs.rb +66 -0
- data/lib/rsmp/proxy/proxy.rb +199 -0
- data/lib/rsmp/proxy/site/modules/aggregated_status.rb +52 -0
- data/lib/rsmp/proxy/site/modules/alarms.rb +27 -0
- data/lib/rsmp/proxy/site/modules/commands.rb +31 -0
- data/lib/rsmp/proxy/site/modules/status.rb +110 -0
- data/lib/rsmp/proxy/site/site_proxy.rb +205 -0
- data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +47 -0
- data/lib/rsmp/proxy/supervisor/modules/alarms.rb +73 -0
- data/lib/rsmp/proxy/supervisor/modules/commands.rb +53 -0
- data/lib/rsmp/proxy/supervisor/modules/status.rb +204 -0
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +178 -0
- data/lib/rsmp/tlc/detector_logic.rb +18 -34
- data/lib/rsmp/tlc/input_states.rb +126 -0
- data/lib/rsmp/tlc/modules/detector_logics.rb +50 -0
- data/lib/rsmp/tlc/modules/display.rb +78 -0
- data/lib/rsmp/tlc/modules/helpers.rb +41 -0
- data/lib/rsmp/tlc/modules/inputs.rb +173 -0
- data/lib/rsmp/tlc/modules/modes.rb +253 -0
- data/lib/rsmp/tlc/modules/outputs.rb +30 -0
- data/lib/rsmp/tlc/modules/plans.rb +218 -0
- data/lib/rsmp/tlc/modules/signal_groups.rb +109 -0
- data/lib/rsmp/tlc/modules/startup_sequence.rb +22 -0
- data/lib/rsmp/tlc/modules/system.rb +140 -0
- data/lib/rsmp/tlc/modules/traffic_data.rb +49 -0
- data/lib/rsmp/tlc/signal_group.rb +37 -41
- data/lib/rsmp/tlc/signal_plan.rb +14 -11
- data/lib/rsmp/tlc/signal_priority.rb +39 -35
- data/lib/rsmp/tlc/startup_sequence.rb +59 -0
- data/lib/rsmp/tlc/traffic_controller.rb +38 -1010
- data/lib/rsmp/tlc/traffic_controller_site.rb +58 -57
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +82 -48
- data/rsmp.gemspec +24 -31
- metadata +79 -139
- data/lib/rsmp/archive.rb +0 -76
- data/lib/rsmp/collect/message_matchers.rb +0 -0
- data/lib/rsmp/component_base.rb +0 -87
- data/lib/rsmp/component_proxy.rb +0 -57
- data/lib/rsmp/components.rb +0 -65
- data/lib/rsmp/error.rb +0 -71
- data/lib/rsmp/logger.rb +0 -216
- data/lib/rsmp/proxy.rb +0 -693
- data/lib/rsmp/site.rb +0 -188
- data/lib/rsmp/site_proxy.rb +0 -389
- data/lib/rsmp/supervisor.rb +0 -302
- data/lib/rsmp/supervisor_proxy.rb +0 -510
- 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
|