rsmp 0.6.2 → 0.7.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/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/config/supervisor.yaml +1 -9
- data/config/tlc.yaml +17 -13
- data/lib/rsmp/archive.rb +1 -1
- data/lib/rsmp/cli.rb +1 -1
- data/lib/rsmp/component.rb +11 -10
- data/lib/rsmp/components.rb +2 -1
- data/lib/rsmp/error.rb +3 -0
- data/lib/rsmp/logger.rb +26 -10
- data/lib/rsmp/logging.rb +1 -1
- data/lib/rsmp/message.rb +1 -1
- data/lib/rsmp/proxy.rb +8 -5
- data/lib/rsmp/supervisor_proxy.rb +11 -4
- data/lib/rsmp/tlc/detector_logic.rb +101 -0
- data/lib/rsmp/tlc/signal_group.rb +85 -0
- data/lib/rsmp/tlc/signal_plan.rb +29 -0
- data/lib/rsmp/tlc/traffic_controller.rb +652 -0
- data/lib/rsmp/tlc/traffic_controller_site.rb +136 -0
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +5 -1
- metadata +7 -3
- data/lib/rsmp/tlc.rb +0 -920
data/lib/rsmp/tlc.rb
DELETED
@@ -1,920 +0,0 @@
|
|
1
|
-
# Simulates a Traffic Light Controller
|
2
|
-
|
3
|
-
module RSMP
|
4
|
-
|
5
|
-
class TrafficController < Component
|
6
|
-
attr_reader :pos, :cycle_time
|
7
|
-
|
8
|
-
def initialize node:, id:, cycle_time: 10
|
9
|
-
super node: node, id: id, grouped: true
|
10
|
-
@signal_groups = []
|
11
|
-
@detector_logics = []
|
12
|
-
@plans = []
|
13
|
-
@cycle_time = cycle_time
|
14
|
-
@num_traffic_situations = 1
|
15
|
-
@num_inputs = 8
|
16
|
-
reset
|
17
|
-
end
|
18
|
-
|
19
|
-
def reset
|
20
|
-
@pos = 0
|
21
|
-
@plan = 0
|
22
|
-
@dark_mode = false
|
23
|
-
@yellow_flash = false
|
24
|
-
@booting = false
|
25
|
-
@control_mode = 'control'
|
26
|
-
@police_key = 0
|
27
|
-
@intersection = 0
|
28
|
-
@is_starting = false
|
29
|
-
@emergency_route = false
|
30
|
-
@emergency_route_number = 0
|
31
|
-
@traffic_situation = 0
|
32
|
-
@manual_control = false
|
33
|
-
@fixed_time_control = false
|
34
|
-
@isolated_control = false
|
35
|
-
@yellow_flash = false
|
36
|
-
@all_red = false
|
37
|
-
|
38
|
-
@inputs = '0'*@num_inputs
|
39
|
-
@input_activations = '0'*@num_inputs
|
40
|
-
@input_results = '0'*@num_inputs
|
41
|
-
end
|
42
|
-
|
43
|
-
def clock
|
44
|
-
node.clock
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
def add_signal_group group
|
49
|
-
@signal_groups << group
|
50
|
-
end
|
51
|
-
|
52
|
-
def add_detector_logic logic
|
53
|
-
@detector_logics << logic
|
54
|
-
end
|
55
|
-
|
56
|
-
def timer now
|
57
|
-
# TODO
|
58
|
-
# We should use a monotone timer, to avoid jumps
|
59
|
-
# in case the user sets the system time
|
60
|
-
pos = Time.now.to_i % @cycle_time
|
61
|
-
if pos != @pos
|
62
|
-
@pos = pos
|
63
|
-
move pos
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def move pos
|
68
|
-
@signal_groups.each do |group|
|
69
|
-
group.move pos
|
70
|
-
end
|
71
|
-
if pos == 0
|
72
|
-
aggregated_status_changed
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def output_states
|
77
|
-
str = @signal_groups.map do |group|
|
78
|
-
s = "#{group.c_id}:#{group.state}"
|
79
|
-
if group.state =~ /^[1-9]$/
|
80
|
-
s.colorize(:green)
|
81
|
-
elsif group.state =~ /^[NOP]$/
|
82
|
-
s.colorize(:yellow)
|
83
|
-
else
|
84
|
-
s.colorize(:red)
|
85
|
-
end
|
86
|
-
end.join ' '
|
87
|
-
print "\t#{pos.to_s.ljust(3)} #{str}\r"
|
88
|
-
end
|
89
|
-
|
90
|
-
def format_signal_group_status
|
91
|
-
if @yellow_flash
|
92
|
-
'c' * @signal_groups.size
|
93
|
-
else
|
94
|
-
@signal_groups.map { |group| group.state }.join
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def handle_command command_code, arg
|
99
|
-
case command_code
|
100
|
-
when 'M0001', 'M0002', 'M0003', 'M0004', 'M0005', 'M0006', 'M0007',
|
101
|
-
'M0012', 'M0013', 'M0014', 'M0015', 'M0016', 'M0017', 'M0018',
|
102
|
-
'M0019', 'M0020', 'M0021', 'M0022',
|
103
|
-
'M0103', 'M0104'
|
104
|
-
|
105
|
-
return send("handle_#{command_code.downcase}", arg)
|
106
|
-
else
|
107
|
-
raise UnknownCommand.new "Unknown command #{command_code}"
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def handle_m0001 arg
|
112
|
-
@node.verify_security_code 2, arg['securityCode']
|
113
|
-
switch_mode arg['status']
|
114
|
-
end
|
115
|
-
|
116
|
-
def handle_m0002 arg
|
117
|
-
@node.verify_security_code 2, arg['securityCode']
|
118
|
-
if RSMP::Tlc.from_rsmp_bool(arg['status'])
|
119
|
-
switch_plan arg['timeplan']
|
120
|
-
else
|
121
|
-
switch_plan 0 # TODO use clock/calender
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def handle_m0003 arg
|
126
|
-
@node.verify_security_code 2, arg['securityCode']
|
127
|
-
@traffic_situation = arg['traficsituation'].to_i
|
128
|
-
end
|
129
|
-
|
130
|
-
def handle_m0004 arg
|
131
|
-
@node.verify_security_code 2, arg['securityCode']
|
132
|
-
# don't restart immeediately, since we need to first send command response
|
133
|
-
# instead, defer an action, which will be handled by the TLC site
|
134
|
-
log "Sheduling restart of TLC", level: :info
|
135
|
-
@node.defer :restart
|
136
|
-
end
|
137
|
-
|
138
|
-
def handle_m0005 arg
|
139
|
-
@node.verify_security_code 2, arg['securityCode']
|
140
|
-
@emergency_route = (arg['status'] == 'True')
|
141
|
-
@emergency_route_number = arg['emergencyroute'].to_i
|
142
|
-
|
143
|
-
if @emergency_route
|
144
|
-
log "Switching to emergency route #{@emergency_route_number}", level: :info
|
145
|
-
else
|
146
|
-
log "Switching off emergency route", level: :info
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def handle_m0006 arg
|
151
|
-
@node.verify_security_code 2, arg['securityCode']
|
152
|
-
input = arg['input'].to_i
|
153
|
-
idx = input - 1
|
154
|
-
return unless idx>=0 && input<@num_inputs # TODO should NotAck
|
155
|
-
@input_activations[idx] = (arg['status']=='True' ? '1' : '0')
|
156
|
-
result = @input_activations[idx]=='1' || @inputs[idx]=='1'
|
157
|
-
@input_results[idx] = (result ? '1' : '0')
|
158
|
-
if @input_activations[idx]
|
159
|
-
log "Activate input #{idx}", level: :info
|
160
|
-
else
|
161
|
-
log "Deactivate input #{idx}", level: :info
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
def handle_m0007 arg
|
166
|
-
@node.verify_security_code 2, arg['securityCode']
|
167
|
-
set_fixed_time_control arg['status']
|
168
|
-
end
|
169
|
-
|
170
|
-
def handle_m0012 arg
|
171
|
-
@node.verify_security_code 2, arg['securityCode']
|
172
|
-
end
|
173
|
-
|
174
|
-
def handle_m0013 arg
|
175
|
-
@node.verify_security_code 2, arg['securityCode']
|
176
|
-
end
|
177
|
-
|
178
|
-
def handle_m0014 arg
|
179
|
-
@node.verify_security_code 2, arg['securityCode']
|
180
|
-
end
|
181
|
-
|
182
|
-
def handle_m0015 arg
|
183
|
-
@node.verify_security_code 2, arg['securityCode']
|
184
|
-
end
|
185
|
-
|
186
|
-
def handle_m0016 arg
|
187
|
-
@node.verify_security_code 2, arg['securityCode']
|
188
|
-
end
|
189
|
-
|
190
|
-
def handle_m0017 arg
|
191
|
-
@node.verify_security_code 2, arg['securityCode']
|
192
|
-
end
|
193
|
-
|
194
|
-
def handle_m0018 arg
|
195
|
-
@node.verify_security_code 2, arg['securityCode']
|
196
|
-
end
|
197
|
-
|
198
|
-
def handle_m0019 arg
|
199
|
-
@node.verify_security_code 2, arg['securityCode']
|
200
|
-
end
|
201
|
-
|
202
|
-
def handle_m0020 arg
|
203
|
-
@node.verify_security_code 2, arg['securityCode']
|
204
|
-
end
|
205
|
-
|
206
|
-
def handle_m0021 arg
|
207
|
-
@node.verify_security_code 2, arg['securityCode']
|
208
|
-
end
|
209
|
-
|
210
|
-
def handle_m0103 arg
|
211
|
-
level = {'Level1'=>1,'Level2'=>2}[arg['status']]
|
212
|
-
@node.change_security_code level, arg['oldSecurityCode'], arg['newSecurityCode']
|
213
|
-
end
|
214
|
-
|
215
|
-
def handle_m0104 arg
|
216
|
-
@node.verify_security_code 1, arg['securityCode']
|
217
|
-
time = Time.new(
|
218
|
-
arg['year'],
|
219
|
-
arg['month'],
|
220
|
-
arg['day'],
|
221
|
-
arg['hour'],
|
222
|
-
arg['minute'],
|
223
|
-
arg['second'],
|
224
|
-
'UTC'
|
225
|
-
)
|
226
|
-
@node.clock.set time
|
227
|
-
log "Clock set to #{time}, (adjustment is #{@node.clock.adjustment}s)", level: :info
|
228
|
-
end
|
229
|
-
|
230
|
-
def set_input i, value
|
231
|
-
return unless i>=0 && i<@num_inputs
|
232
|
-
@inputs[i] = (arg['value'] ? '1' : '0')
|
233
|
-
end
|
234
|
-
|
235
|
-
def set_fixed_time_control status
|
236
|
-
@fixed_time_control = status
|
237
|
-
end
|
238
|
-
|
239
|
-
def switch_plan plan
|
240
|
-
plan_nr = plan.to_i
|
241
|
-
if plan_nr == 0
|
242
|
-
log "Switching to plan selection by time table", level: :info
|
243
|
-
else
|
244
|
-
log "Switching to plan #{plan_nr}", level: :info
|
245
|
-
end
|
246
|
-
@plan = plan_nr
|
247
|
-
end
|
248
|
-
|
249
|
-
def switch_mode mode
|
250
|
-
log "Switching to mode #{mode}", level: :info
|
251
|
-
case mode
|
252
|
-
when 'NormalControl'
|
253
|
-
@yellow_flash = false
|
254
|
-
@dark_mode = false
|
255
|
-
when 'YellowFlash'
|
256
|
-
@yellow_flash = true
|
257
|
-
@dark_mode = false
|
258
|
-
when 'Dark'
|
259
|
-
@yellow_flash = false
|
260
|
-
@dark_mode = true
|
261
|
-
end
|
262
|
-
mode
|
263
|
-
end
|
264
|
-
|
265
|
-
def get_status code, name=nil
|
266
|
-
case code
|
267
|
-
when 'S0001', 'S0002', 'S0003', 'S0004', 'S0005', 'S0006', 'S0007',
|
268
|
-
'S0008', 'S0009', 'S0010', 'S0011', 'S0012', 'S0013', 'S0014',
|
269
|
-
'S0015', 'S0016', 'S0017', 'S0018', 'S0019', 'S0020', 'S0021',
|
270
|
-
'S0022', 'S0023', 'S0024', 'S0026', 'S0027', 'S0028',
|
271
|
-
'S0029', 'S0030', 'S0031',
|
272
|
-
'S0091', 'S0092', 'S0095', 'S0096', 'S0097',
|
273
|
-
'S0205', 'S0206', 'S0207', 'S0208'
|
274
|
-
return send("handle_#{code.downcase}", code, name)
|
275
|
-
else
|
276
|
-
raise InvalidMessage.new "unknown status code #{code}"
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
def handle_s0001 status_code, status_name=nil
|
281
|
-
case status_name
|
282
|
-
when 'signalgroupstatus'
|
283
|
-
RSMP::Tlc.make_status format_signal_group_status
|
284
|
-
when 'cyclecounter'
|
285
|
-
RSMP::Tlc.make_status @pos.to_s
|
286
|
-
when 'basecyclecounter'
|
287
|
-
RSMP::Tlc.make_status @pos.to_s
|
288
|
-
when 'stage'
|
289
|
-
RSMP::Tlc.make_status 0.to_s
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
def handle_s0002 status_code, status_name=nil
|
294
|
-
case status_name
|
295
|
-
when 'detectorlogicstatus'
|
296
|
-
RSMP::Tlc.make_status @detector_logics.map { |dl| dl.value ? '1' : '0' }.join
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
def handle_s0003 status_code, status_name=nil
|
301
|
-
case status_name
|
302
|
-
when 'inputstatus'
|
303
|
-
RSMP::Tlc.make_status @input_results
|
304
|
-
when 'extendedinputstatus'
|
305
|
-
RSMP::Tlc.make_status 0.to_s
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
def handle_s0004 status_code, status_name=nil
|
310
|
-
case status_name
|
311
|
-
when 'outputstatus'
|
312
|
-
RSMP::Tlc.make_status 0
|
313
|
-
when 'extendedoutputstatus'
|
314
|
-
RSMP::Tlc.make_status 0
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
def handle_s0005 status_code, status_name=nil
|
319
|
-
case status_name
|
320
|
-
when 'status'
|
321
|
-
RSMP::Tlc.make_status @is_starting
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
def handle_s0006 status_code, status_name=nil
|
326
|
-
case status_name
|
327
|
-
when 'status'
|
328
|
-
RSMP::Tlc.make_status @emergency_route
|
329
|
-
when 'emergencystage'
|
330
|
-
RSMP::Tlc.make_status @emergency_route_number
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
def handle_s0007 status_code, status_name=nil
|
335
|
-
case status_name
|
336
|
-
when 'intersection'
|
337
|
-
RSMP::Tlc.make_status @intersection
|
338
|
-
when 'status'
|
339
|
-
RSMP::Tlc.make_status !@dark_mode
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
def handle_s0008 status_code, status_name=nil
|
344
|
-
case status_name
|
345
|
-
when 'intersection'
|
346
|
-
RSMP::Tlc.make_status @intersection
|
347
|
-
when 'status'
|
348
|
-
RSMP::Tlc.make_status @manual_control
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
def handle_s0009 status_code, status_name=nil
|
353
|
-
case status_name
|
354
|
-
when 'intersection'
|
355
|
-
RSMP::Tlc.make_status @intersection
|
356
|
-
when 'status'
|
357
|
-
RSMP::Tlc.make_status @fixed_time_control
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
def handle_s0010 status_code, status_name=nil
|
362
|
-
case status_name
|
363
|
-
when 'intersection'
|
364
|
-
RSMP::Tlc.make_status @intersection
|
365
|
-
when 'status'
|
366
|
-
RSMP::Tlc.make_status @isolated_control
|
367
|
-
end
|
368
|
-
end
|
369
|
-
|
370
|
-
def handle_s0011 status_code, status_name=nil
|
371
|
-
case status_name
|
372
|
-
when 'intersection'
|
373
|
-
RSMP::Tlc.make_status @intersection
|
374
|
-
when 'status'
|
375
|
-
RSMP::Tlc.make_status @yellow_flash
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
def handle_s0012 status_code, status_name=nil
|
380
|
-
case status_name
|
381
|
-
when 'intersection'
|
382
|
-
RSMP::Tlc.make_status @intersection
|
383
|
-
when 'status'
|
384
|
-
RSMP::Tlc.make_status @all_red
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
def handle_s0013 status_code, status_name=nil
|
389
|
-
case status_name
|
390
|
-
when 'intersection'
|
391
|
-
RSMP::Tlc.make_status @intersection
|
392
|
-
when 'status'
|
393
|
-
RSMP::Tlc.make_status @police_key
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
def handle_s0014 status_code, status_name=nil
|
398
|
-
case status_name
|
399
|
-
when 'status'
|
400
|
-
RSMP::Tlc.make_status @plan
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
|
-
def handle_s0015 status_code, status_name=nil
|
405
|
-
case status_name
|
406
|
-
when 'status'
|
407
|
-
RSMP::Tlc.make_status @traffic_situation
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
def handle_s0016 status_code, status_name=nil
|
412
|
-
case status_name
|
413
|
-
when 'number'
|
414
|
-
RSMP::Tlc.make_status @detector_logics.size
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
def handle_s0017 status_code, status_name=nil
|
419
|
-
case status_name
|
420
|
-
when 'number'
|
421
|
-
RSMP::Tlc.make_status @signal_groups.size
|
422
|
-
end
|
423
|
-
end
|
424
|
-
|
425
|
-
def handle_s0018 status_code, status_name=nil
|
426
|
-
case status_name
|
427
|
-
when 'number'
|
428
|
-
RSMP::Tlc.make_status @plans.size
|
429
|
-
end
|
430
|
-
end
|
431
|
-
|
432
|
-
def handle_s0019 status_code, status_name=nil
|
433
|
-
case status_name
|
434
|
-
when 'number'
|
435
|
-
RSMP::Tlc.make_status @num_traffic_situations
|
436
|
-
end
|
437
|
-
end
|
438
|
-
|
439
|
-
def handle_s0020 status_code, status_name=nil
|
440
|
-
case status_name
|
441
|
-
when 'intersection'
|
442
|
-
RSMP::Tlc.make_status @intersection
|
443
|
-
when 'controlmode'
|
444
|
-
RSMP::Tlc.make_status @control_mode
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
def handle_s0021 status_code, status_name=nil
|
449
|
-
case status_name
|
450
|
-
when 'detectorlogics'
|
451
|
-
RSMP::Tlc.make_status @detector_logics.map { |logic| logic.forced=='True' ? '1' : '0'}.join
|
452
|
-
end
|
453
|
-
end
|
454
|
-
|
455
|
-
def handle_s0022 status_code, status_name=nil
|
456
|
-
case status_name
|
457
|
-
when 'status'
|
458
|
-
RSMP::Tlc.make_status '1'
|
459
|
-
end
|
460
|
-
end
|
461
|
-
|
462
|
-
def handle_s0023 status_code, status_name=nil
|
463
|
-
case status_name
|
464
|
-
when 'status'
|
465
|
-
RSMP::Tlc.make_status '1-1-0'
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
def handle_s0024 status_code, status_name=nil
|
470
|
-
case status_name
|
471
|
-
when 'status'
|
472
|
-
RSMP::Tlc.make_status '1-0'
|
473
|
-
end
|
474
|
-
end
|
475
|
-
|
476
|
-
def handle_s0026 status_code, status_name=nil
|
477
|
-
case status_name
|
478
|
-
when 'status'
|
479
|
-
RSMP::Tlc.make_status '0-00'
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
def handle_s0027 status_code, status_name=nil
|
484
|
-
case status_name
|
485
|
-
when 'status'
|
486
|
-
RSMP::Tlc.make_status '00-00-00-00'
|
487
|
-
end
|
488
|
-
end
|
489
|
-
|
490
|
-
def handle_s0028 status_code, status_name=nil
|
491
|
-
case status_name
|
492
|
-
when 'status'
|
493
|
-
RSMP::Tlc.make_status '00-00'
|
494
|
-
end
|
495
|
-
end
|
496
|
-
|
497
|
-
def handle_s0029 status_code, status_name=nil
|
498
|
-
case status_name
|
499
|
-
when 'status'
|
500
|
-
RSMP::Tlc.make_status ''
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
|
-
def handle_s0030 status_code, status_name=nil
|
505
|
-
case status_name
|
506
|
-
when 'status'
|
507
|
-
RSMP::Tlc.make_status ''
|
508
|
-
end
|
509
|
-
end
|
510
|
-
|
511
|
-
def handle_s0031 status_code, status_name=nil
|
512
|
-
case status_name
|
513
|
-
when 'status'
|
514
|
-
RSMP::Tlc.make_status ''
|
515
|
-
end
|
516
|
-
end
|
517
|
-
|
518
|
-
def handle_s0091 status_code, status_name=nil
|
519
|
-
case status_name
|
520
|
-
when 'user'
|
521
|
-
RSMP::Tlc.make_status 'nobody'
|
522
|
-
when 'status'
|
523
|
-
RSMP::Tlc.make_status 'logout'
|
524
|
-
end
|
525
|
-
end
|
526
|
-
|
527
|
-
def handle_s0092 status_code, status_name=nil
|
528
|
-
case status_name
|
529
|
-
when 'user'
|
530
|
-
RSMP::Tlc.make_status 'nobody'
|
531
|
-
when 'status'
|
532
|
-
RSMP::Tlc.make_status 'logout'
|
533
|
-
end
|
534
|
-
end
|
535
|
-
|
536
|
-
def handle_s0095 status_code, status_name=nil
|
537
|
-
case status_name
|
538
|
-
when 'status'
|
539
|
-
RSMP::Tlc.make_status RSMP::VERSION
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
def handle_s0096 status_code, status_name=nil
|
544
|
-
now = clock.now
|
545
|
-
case status_name
|
546
|
-
when 'year'
|
547
|
-
RSMP::Tlc.make_status now.year.to_s.rjust(4, "0")
|
548
|
-
when 'month'
|
549
|
-
RSMP::Tlc.make_status now.month.to_s.rjust(2, "0")
|
550
|
-
when 'day'
|
551
|
-
RSMP::Tlc.make_status now.day.to_s.rjust(2, "0")
|
552
|
-
when 'hour'
|
553
|
-
RSMP::Tlc.make_status now.hour.to_s.rjust(2, "0")
|
554
|
-
when 'minute'
|
555
|
-
RSMP::Tlc.make_status now.min.to_s.rjust(2, "0")
|
556
|
-
when 'second'
|
557
|
-
RSMP::Tlc.make_status now.sec.to_s.rjust(2, "0")
|
558
|
-
end
|
559
|
-
end
|
560
|
-
|
561
|
-
def handle_s0097 status_code, status_name=nil
|
562
|
-
case status_name
|
563
|
-
when 'checksum'
|
564
|
-
RSMP::Tlc.make_status '1'
|
565
|
-
when 'timestamp'
|
566
|
-
now = @node.clock.to_s
|
567
|
-
RSMP::Tlc.make_status now
|
568
|
-
end
|
569
|
-
end
|
570
|
-
|
571
|
-
def handle_s0205 status_code, status_name=nil
|
572
|
-
case status_name
|
573
|
-
when 'start'
|
574
|
-
RSMP::Tlc.make_status clock.to_s
|
575
|
-
when 'vehicles'
|
576
|
-
RSMP::Tlc.make_status 0
|
577
|
-
end
|
578
|
-
end
|
579
|
-
|
580
|
-
def handle_s0206 status_code, status_name=nil
|
581
|
-
case status_name
|
582
|
-
when 'start'
|
583
|
-
RSMP::Tlc.make_status clock.to_s
|
584
|
-
when 'speed'
|
585
|
-
RSMP::Tlc.make_status 0
|
586
|
-
end
|
587
|
-
end
|
588
|
-
|
589
|
-
def handle_s0207 status_code, status_name=nil
|
590
|
-
case status_name
|
591
|
-
when 'start'
|
592
|
-
RSMP::Tlc.make_status clock.to_s
|
593
|
-
when 'occupancy'
|
594
|
-
RSMP::Tlc.make_status 0
|
595
|
-
end
|
596
|
-
end
|
597
|
-
|
598
|
-
def handle_s0208 status_code, status_name=nil
|
599
|
-
case status_name
|
600
|
-
when 'start'
|
601
|
-
RSMP::Tlc.make_status clock.to_s
|
602
|
-
when 'P'
|
603
|
-
RSMP::Tlc.make_status 0
|
604
|
-
when 'PS'
|
605
|
-
RSMP::Tlc.make_status 0
|
606
|
-
when 'L'
|
607
|
-
RSMP::Tlc.make_status 0
|
608
|
-
when 'LS'
|
609
|
-
RSMP::Tlc.make_status 0
|
610
|
-
when 'B'
|
611
|
-
RSMP::Tlc.make_status 0
|
612
|
-
when 'SP'
|
613
|
-
RSMP::Tlc.make_status 0
|
614
|
-
when 'MC'
|
615
|
-
RSMP::Tlc.make_status 0
|
616
|
-
when 'C'
|
617
|
-
RSMP::Tlc.make_status 0
|
618
|
-
when 'F'
|
619
|
-
RSMP::Tlc.make_status 0
|
620
|
-
end
|
621
|
-
end
|
622
|
-
|
623
|
-
end
|
624
|
-
|
625
|
-
class SignalGroup < Component
|
626
|
-
attr_reader :plan, :state
|
627
|
-
|
628
|
-
# plan is a string, with each character representing a signal phase at a particular second in the cycle
|
629
|
-
def initialize node:, id:, plan: nil
|
630
|
-
super node: node, id: id, grouped: false
|
631
|
-
@plan = plan
|
632
|
-
move 0
|
633
|
-
end
|
634
|
-
|
635
|
-
def get_state pos
|
636
|
-
return 'a' unless @plan # if no plan, use phase a, which means disabled/dark
|
637
|
-
if pos > @plan.length
|
638
|
-
'.'
|
639
|
-
else
|
640
|
-
@plan[pos]
|
641
|
-
end
|
642
|
-
end
|
643
|
-
|
644
|
-
def move pos
|
645
|
-
@state = get_state pos
|
646
|
-
end
|
647
|
-
|
648
|
-
def handle_command command_code, arg
|
649
|
-
case command_code
|
650
|
-
when 'M0010', 'M0011'
|
651
|
-
return send("handle_#{command_code.downcase}", arg)
|
652
|
-
else
|
653
|
-
raise UnknownCommand.new "Unknown command #{command_code}"
|
654
|
-
end
|
655
|
-
end
|
656
|
-
|
657
|
-
# Start of signal group. Orders a signal group to green
|
658
|
-
def handle_m0010 arg
|
659
|
-
@node.verify_security_code 2, arg['securityCode']
|
660
|
-
if RSMP::Tlc.from_rsmp_bool arg['status']
|
661
|
-
log "Start signal group #{c_id}, go to green", level: :info
|
662
|
-
end
|
663
|
-
end
|
664
|
-
|
665
|
-
# Stop of signal group. Orders a signal group to red
|
666
|
-
def handle_m0011 arg
|
667
|
-
@node.verify_security_code 2, arg['securityCode']
|
668
|
-
if RSMP::Tlc.from_rsmp_bool arg['status']
|
669
|
-
log "Stop signal group #{c_id}, go to red", level: :info
|
670
|
-
end
|
671
|
-
end
|
672
|
-
|
673
|
-
def get_status code, name=nil
|
674
|
-
case code
|
675
|
-
when 'S0025'
|
676
|
-
return send("handle_#{code.downcase}", code, name)
|
677
|
-
else
|
678
|
-
raise InvalidMessage.new "unknown status code #{code}"
|
679
|
-
end
|
680
|
-
end
|
681
|
-
|
682
|
-
def handle_s0025 status_code, status_name=nil
|
683
|
-
now = @node.clock.to_s
|
684
|
-
case status_name
|
685
|
-
when 'minToGEstimate'
|
686
|
-
RSMP::Tlc.make_status now
|
687
|
-
when 'maxToGEstimate'
|
688
|
-
RSMP::Tlc.make_status now
|
689
|
-
when 'likelyToGEstimate'
|
690
|
-
RSMP::Tlc.make_status now
|
691
|
-
when 'ToGConfidence'
|
692
|
-
RSMP::Tlc.make_status 0
|
693
|
-
when 'minToREstimate'
|
694
|
-
RSMP::Tlc.make_status now
|
695
|
-
when 'maxToREstimate'
|
696
|
-
RSMP::Tlc.make_status now
|
697
|
-
when 'likelyToREstimate'
|
698
|
-
RSMP::Tlc.make_status now
|
699
|
-
when 'ToRConfidence'
|
700
|
-
RSMP::Tlc.make_status 0
|
701
|
-
end
|
702
|
-
end
|
703
|
-
end
|
704
|
-
|
705
|
-
class DetectorLogic < Component
|
706
|
-
attr_reader :forced, :value
|
707
|
-
|
708
|
-
def initialize node:, id:
|
709
|
-
super node: node, id: id, grouped: false
|
710
|
-
@forced = 0
|
711
|
-
@value = 0
|
712
|
-
end
|
713
|
-
|
714
|
-
def get_status code, name=nil
|
715
|
-
case code
|
716
|
-
when 'S0201', 'S0202', 'S0203', 'S0204'
|
717
|
-
return send("handle_#{code.downcase}", code, name)
|
718
|
-
else
|
719
|
-
raise InvalidMessage.new "unknown status code #{code}"
|
720
|
-
end
|
721
|
-
end
|
722
|
-
|
723
|
-
def handle_s0201 status_code, status_name=nil
|
724
|
-
case status_name
|
725
|
-
when 'starttime'
|
726
|
-
RSMP::Tlc.make_status @node.clock.to_s
|
727
|
-
when 'vehicles'
|
728
|
-
RSMP::Tlc.make_status 0
|
729
|
-
end
|
730
|
-
end
|
731
|
-
|
732
|
-
def handle_s0202 status_code, status_name=nil
|
733
|
-
case status_name
|
734
|
-
when 'starttime'
|
735
|
-
RSMP::Tlc.make_status @node.clock.to_s
|
736
|
-
when 'speed'
|
737
|
-
RSMP::Tlc.make_status 0
|
738
|
-
end
|
739
|
-
end
|
740
|
-
|
741
|
-
def handle_s0203 status_code, status_name=nil
|
742
|
-
case status_name
|
743
|
-
when 'starttime'
|
744
|
-
RSMP::Tlc.make_status @node.clock.to_s
|
745
|
-
when 'occupancy'
|
746
|
-
RSMP::Tlc.make_status 0
|
747
|
-
end
|
748
|
-
end
|
749
|
-
|
750
|
-
def handle_s0204 status_code, status_name=nil
|
751
|
-
case status_name
|
752
|
-
when 'starttime'
|
753
|
-
RSMP::Tlc.make_status @node.clock.to_s
|
754
|
-
when 'P'
|
755
|
-
RSMP::Tlc.make_status 0
|
756
|
-
when 'PS'
|
757
|
-
RSMP::Tlc.make_status 0
|
758
|
-
when 'L'
|
759
|
-
RSMP::Tlc.make_status 0
|
760
|
-
when 'LS'
|
761
|
-
RSMP::Tlc.make_status 0
|
762
|
-
when 'B'
|
763
|
-
RSMP::Tlc.make_status 0
|
764
|
-
when 'SP'
|
765
|
-
RSMP::Tlc.make_status 0
|
766
|
-
when 'MC'
|
767
|
-
RSMP::Tlc.make_status 0
|
768
|
-
when 'C'
|
769
|
-
RSMP::Tlc.make_status 0
|
770
|
-
when 'F'
|
771
|
-
RSMP::Tlc.make_status 0
|
772
|
-
end
|
773
|
-
end
|
774
|
-
|
775
|
-
def handle_command command_code, arg
|
776
|
-
case command_code
|
777
|
-
when 'M0008'
|
778
|
-
handle_m0008 arg
|
779
|
-
else
|
780
|
-
raise UnknownCommand.new "Unknown command #{command_code}"
|
781
|
-
end
|
782
|
-
end
|
783
|
-
|
784
|
-
def handle_m0008 arg
|
785
|
-
@node.verify_security_code 2, arg['securityCode']
|
786
|
-
status = arg['status']=='True'
|
787
|
-
mode = arg['mode']=='True'
|
788
|
-
force_detector_logic status, mode
|
789
|
-
arg
|
790
|
-
end
|
791
|
-
|
792
|
-
def force_detector_logic forced, value
|
793
|
-
@forced = forced
|
794
|
-
@value = value
|
795
|
-
if @forced
|
796
|
-
log "Forcing to #{value}", level: :info
|
797
|
-
else
|
798
|
-
log "Releasing", level: :info
|
799
|
-
end
|
800
|
-
end
|
801
|
-
|
802
|
-
end
|
803
|
-
|
804
|
-
class Tlc < Site
|
805
|
-
attr_accessor :main
|
806
|
-
def initialize options={}
|
807
|
-
super options
|
808
|
-
@sxl = 'traffic_light_controller'
|
809
|
-
@security_codes = options[:site_settings]['security_codes']
|
810
|
-
@interval = options[:site_settings].dig('intervals','timer') || 1
|
811
|
-
unless @main
|
812
|
-
raise ConfigurationError.new "TLC must have a main component"
|
813
|
-
end
|
814
|
-
end
|
815
|
-
|
816
|
-
def build_component id:, type:, settings:{}
|
817
|
-
component = case type
|
818
|
-
when 'main'
|
819
|
-
@main = TrafficController.new node: self, id: id, cycle_time: settings['cycle_time']
|
820
|
-
when 'signal_group'
|
821
|
-
group = SignalGroup.new node: self, id: id, plan: settings['plan']
|
822
|
-
@main.add_signal_group group
|
823
|
-
group
|
824
|
-
when 'detector_logic'
|
825
|
-
logic = DetectorLogic.new node: self, id: id
|
826
|
-
@main.add_detector_logic logic
|
827
|
-
logic
|
828
|
-
end
|
829
|
-
end
|
830
|
-
|
831
|
-
def start_action
|
832
|
-
super
|
833
|
-
start_timer
|
834
|
-
end
|
835
|
-
|
836
|
-
def start_timer
|
837
|
-
task_name = "tlc timer"
|
838
|
-
log "Starting #{task_name} with interval #{@interval} seconds", level: :debug
|
839
|
-
|
840
|
-
@timer = @task.async do |task|
|
841
|
-
task.annotate task_name
|
842
|
-
next_time = Time.now.to_f
|
843
|
-
loop do
|
844
|
-
begin
|
845
|
-
timer(@clock.now)
|
846
|
-
rescue EOFError => e
|
847
|
-
log "Connection closed: #{e}", level: :warning
|
848
|
-
rescue IOError => e
|
849
|
-
log "IOError", level: :warning
|
850
|
-
rescue Errno::ECONNRESET
|
851
|
-
log "Connection reset by peer", level: :warning
|
852
|
-
rescue Errno::EPIPE => e
|
853
|
-
log "Broken pipe", level: :warning
|
854
|
-
rescue StandardError => e
|
855
|
-
notify_error e, level: :internal
|
856
|
-
ensure
|
857
|
-
# adjust sleep duration to avoid drift. so wake up always happens on the
|
858
|
-
# same fractional second.
|
859
|
-
# note that Time.now is not monotonic. If the clock is changed,
|
860
|
-
# either manaully or via NTP, the sleep interval might jump.
|
861
|
-
# an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
|
862
|
-
# to get the current time. this ensures a constant interval, but
|
863
|
-
# if the clock is changed, the wake up would then happen on a different
|
864
|
-
# fractional second
|
865
|
-
next_time += @interval
|
866
|
-
duration = next_time - Time.now.to_f
|
867
|
-
task.sleep duration
|
868
|
-
end
|
869
|
-
end
|
870
|
-
end
|
871
|
-
end
|
872
|
-
|
873
|
-
def timer now
|
874
|
-
return unless @main
|
875
|
-
@main.timer now
|
876
|
-
end
|
877
|
-
|
878
|
-
def verify_security_code level, code
|
879
|
-
raise ArgumentError.new("Level must be 1-2, got #{level}") unless (1..2).include?(level)
|
880
|
-
if @security_codes[level] != code
|
881
|
-
raise MessageRejected.new("Wrong security code for level #{level}")
|
882
|
-
end
|
883
|
-
end
|
884
|
-
|
885
|
-
def change_security_code level, old_code, new_code
|
886
|
-
verify_security_code level, old_code
|
887
|
-
@security_codes[level] = new_code
|
888
|
-
end
|
889
|
-
|
890
|
-
def self.to_rmsp_bool bool
|
891
|
-
if bool
|
892
|
-
'True'
|
893
|
-
else
|
894
|
-
'False'
|
895
|
-
end
|
896
|
-
end
|
897
|
-
|
898
|
-
def self.from_rsmp_bool str
|
899
|
-
str == 'True'
|
900
|
-
end
|
901
|
-
|
902
|
-
def self.make_status value, q='recent'
|
903
|
-
case value
|
904
|
-
when true, false
|
905
|
-
return to_rmsp_bool(value), q
|
906
|
-
else
|
907
|
-
return value, q
|
908
|
-
end
|
909
|
-
end
|
910
|
-
|
911
|
-
def do_deferred item
|
912
|
-
case item
|
913
|
-
when :restart
|
914
|
-
log "Restarting TLC", level: :info
|
915
|
-
restart
|
916
|
-
end
|
917
|
-
end
|
918
|
-
|
919
|
-
end
|
920
|
-
end
|