rsmp 0.5.6 → 0.6.3

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.
@@ -0,0 +1,652 @@
1
+ module RSMP
2
+ module TLC
3
+ # TrafficController is the main component of a TrafficControllerSite.
4
+ # It handles all command and status for the main component,
5
+ # and keeps track of signal plans, detector logics, inputs, etc. which do
6
+ # not have dedicated components.
7
+ class TrafficController < Component
8
+ attr_reader :pos, :cycle_time, :plan
9
+
10
+ def initialize node:, id:, cycle_time: 10, signal_plans:
11
+ super node: node, id: id, grouped: true
12
+ @signal_groups = []
13
+ @detector_logics = []
14
+ @plans = signal_plans
15
+ @cycle_time = cycle_time
16
+ @num_traffic_situations = 1
17
+ @num_inputs = 8
18
+ reset
19
+ end
20
+
21
+ def reset
22
+ @pos = 0
23
+ @plan = 0
24
+ @dark_mode = false
25
+ @yellow_flash = false
26
+ @booting = false
27
+ @control_mode = 'control'
28
+ @police_key = 0
29
+ @intersection = 0
30
+ @is_starting = false
31
+ @emergency_route = false
32
+ @emergency_route_number = 0
33
+ @traffic_situation = 0
34
+ @manual_control = false
35
+ @fixed_time_control = false
36
+ @isolated_control = false
37
+ @yellow_flash = false
38
+ @all_red = false
39
+
40
+ @inputs = '0'*@num_inputs
41
+ @input_activations = '0'*@num_inputs
42
+ @input_results = '0'*@num_inputs
43
+ end
44
+
45
+ def clock
46
+ node.clock
47
+ end
48
+
49
+ def current_plan
50
+ # TODO plan 0 should means use time table
51
+ if @plans
52
+ @plans[ plan ] || @plans.values.first
53
+ else
54
+ nil
55
+ end
56
+ end
57
+
58
+ def add_signal_group group
59
+ @signal_groups << group
60
+ end
61
+
62
+ def add_detector_logic logic
63
+ @detector_logics << logic
64
+ end
65
+
66
+ def timer now
67
+ # TODO
68
+ # We should use a monotone timer, to avoid jumps
69
+ # in case the user sets the system time
70
+ pos = Time.now.to_i % @cycle_time
71
+ if pos != @pos
72
+ @pos = pos
73
+ move pos
74
+ end
75
+ end
76
+
77
+ def move pos
78
+ @signal_groups.each do |group|
79
+ group.move pos
80
+ end
81
+ #output_states
82
+ end
83
+
84
+ def output_states
85
+ str = @signal_groups.map do |group|
86
+ s = "#{group.c_id}:#{group.state}"
87
+ if group.state =~ /^[1-9]$/
88
+ s.colorize(:green)
89
+ elsif group.state =~ /^[NOP]$/
90
+ s.colorize(:yellow)
91
+ elsif group.state =~ /^[a]$/
92
+ s.colorize(color: :black)
93
+ else
94
+ s.colorize(:red)
95
+ end
96
+ end.join ' '
97
+ plan = "P#{@plan}"
98
+ print "#{plan.rjust(4)} #{pos.to_s.rjust(4)} #{str}\r"
99
+ end
100
+
101
+ def format_signal_group_status
102
+ if @yellow_flash
103
+ 'c' * @signal_groups.size
104
+ else
105
+ @signal_groups.map { |group| group.state }.join
106
+ end
107
+ end
108
+
109
+ def handle_command command_code, arg
110
+ case command_code
111
+ when 'M0001', 'M0002', 'M0003', 'M0004', 'M0005', 'M0006', 'M0007',
112
+ 'M0012', 'M0013', 'M0014', 'M0015', 'M0016', 'M0017', 'M0018',
113
+ 'M0019', 'M0020', 'M0021', 'M0022',
114
+ 'M0103', 'M0104'
115
+
116
+ return send("handle_#{command_code.downcase}", arg)
117
+ else
118
+ raise UnknownCommand.new "Unknown command #{command_code}"
119
+ end
120
+ end
121
+
122
+ def handle_m0001 arg
123
+ @node.verify_security_code 2, arg['securityCode']
124
+ switch_mode arg['status']
125
+ end
126
+
127
+ def handle_m0002 arg
128
+ @node.verify_security_code 2, arg['securityCode']
129
+ if TrafficControllerSite.from_rsmp_bool(arg['status'])
130
+ switch_plan arg['timeplan']
131
+ else
132
+ switch_plan 0 # TODO use clock/calender
133
+ end
134
+ end
135
+
136
+ def handle_m0003 arg
137
+ @node.verify_security_code 2, arg['securityCode']
138
+ @traffic_situation = arg['traficsituation'].to_i
139
+ end
140
+
141
+ def handle_m0004 arg
142
+ @node.verify_security_code 2, arg['securityCode']
143
+ # don't restart immeediately, since we need to first send command response
144
+ # instead, defer an action, which will be handled by the TLC site
145
+ log "Sheduling restart of TLC", level: :info
146
+ @node.defer :restart
147
+ end
148
+
149
+ def handle_m0005 arg
150
+ @node.verify_security_code 2, arg['securityCode']
151
+ @emergency_route = (arg['status'] == 'True')
152
+ @emergency_route_number = arg['emergencyroute'].to_i
153
+
154
+ if @emergency_route
155
+ log "Switching to emergency route #{@emergency_route_number}", level: :info
156
+ else
157
+ log "Switching off emergency route", level: :info
158
+ end
159
+ end
160
+
161
+ def handle_m0006 arg
162
+ @node.verify_security_code 2, arg['securityCode']
163
+ input = arg['input'].to_i
164
+ idx = input - 1
165
+ return unless idx>=0 && input<@num_inputs # TODO should NotAck
166
+ @input_activations[idx] = (arg['status']=='True' ? '1' : '0')
167
+ result = @input_activations[idx]=='1' || @inputs[idx]=='1'
168
+ @input_results[idx] = (result ? '1' : '0')
169
+ if @input_activations[idx]
170
+ log "Activate input #{idx}", level: :info
171
+ else
172
+ log "Deactivate input #{idx}", level: :info
173
+ end
174
+ end
175
+
176
+ def handle_m0007 arg
177
+ @node.verify_security_code 2, arg['securityCode']
178
+ set_fixed_time_control arg['status']
179
+ end
180
+
181
+ def handle_m0012 arg
182
+ @node.verify_security_code 2, arg['securityCode']
183
+ end
184
+
185
+ def handle_m0013 arg
186
+ @node.verify_security_code 2, arg['securityCode']
187
+ end
188
+
189
+ def find_plan plan_nr
190
+ plan = @plans[plan_nr.to_i]
191
+ raise InvalidMessage.new "unknown signal plan #{plan_nr}, known only [#{@plans.keys.join(',')}]" unless plan
192
+ plan
193
+ end
194
+
195
+ def handle_m0014 arg
196
+ @node.verify_security_code 2, arg['securityCode']
197
+ plan = find_plan arg['plan']
198
+ arg['status'].split(',').each do |item|
199
+ matched = /(\d+)-(\d+)/.match item
200
+ band = matched[1].to_i
201
+ value = matched[2].to_i
202
+ log "Set plan #{arg['plan']} dynamic band #{band} to #{value}", level: :info
203
+ plan.set_band band, value
204
+ end
205
+ end
206
+
207
+ def handle_m0015 arg
208
+ @node.verify_security_code 2, arg['securityCode']
209
+ end
210
+
211
+ def handle_m0016 arg
212
+ @node.verify_security_code 2, arg['securityCode']
213
+ end
214
+
215
+ def handle_m0017 arg
216
+ @node.verify_security_code 2, arg['securityCode']
217
+ end
218
+
219
+ def handle_m0018 arg
220
+ @node.verify_security_code 2, arg['securityCode']
221
+ end
222
+
223
+ def handle_m0019 arg
224
+ @node.verify_security_code 2, arg['securityCode']
225
+ end
226
+
227
+ def handle_m0020 arg
228
+ @node.verify_security_code 2, arg['securityCode']
229
+ end
230
+
231
+ def handle_m0021 arg
232
+ @node.verify_security_code 2, arg['securityCode']
233
+ end
234
+
235
+ def handle_m0103 arg
236
+ level = {'Level1'=>1,'Level2'=>2}[arg['status']]
237
+ @node.change_security_code level, arg['oldSecurityCode'], arg['newSecurityCode']
238
+ end
239
+
240
+ def handle_m0104 arg
241
+ @node.verify_security_code 1, arg['securityCode']
242
+ time = Time.new(
243
+ arg['year'],
244
+ arg['month'],
245
+ arg['day'],
246
+ arg['hour'],
247
+ arg['minute'],
248
+ arg['second'],
249
+ 'UTC'
250
+ )
251
+ @node.clock.set time
252
+ log "Clock set to #{time}, (adjustment is #{@node.clock.adjustment}s)", level: :info
253
+ end
254
+
255
+ def set_input i, value
256
+ return unless i>=0 && i<@num_inputs
257
+ @inputs[i] = (arg['value'] ? '1' : '0')
258
+ end
259
+
260
+ def set_fixed_time_control status
261
+ @fixed_time_control = status
262
+ end
263
+
264
+ def switch_plan plan
265
+ plan_nr = plan.to_i
266
+ if plan_nr == 0
267
+ log "Switching to plan selection by time table", level: :info
268
+ else
269
+ plan = find_plan plan_nr
270
+ log "Switching to plan #{plan_nr}", level: :info
271
+ end
272
+ @plan = plan_nr
273
+ end
274
+
275
+ def switch_mode mode
276
+ log "Switching to mode #{mode}", level: :info
277
+ case mode
278
+ when 'NormalControl'
279
+ @yellow_flash = false
280
+ @dark_mode = false
281
+ when 'YellowFlash'
282
+ @yellow_flash = true
283
+ @dark_mode = false
284
+ when 'Dark'
285
+ @yellow_flash = false
286
+ @dark_mode = true
287
+ end
288
+ mode
289
+ end
290
+
291
+ def get_status code, name=nil
292
+ case code
293
+ when 'S0001', 'S0002', 'S0003', 'S0004', 'S0005', 'S0006', 'S0007',
294
+ 'S0008', 'S0009', 'S0010', 'S0011', 'S0012', 'S0013', 'S0014',
295
+ 'S0015', 'S0016', 'S0017', 'S0018', 'S0019', 'S0020', 'S0021',
296
+ 'S0022', 'S0023', 'S0024', 'S0026', 'S0027', 'S0028',
297
+ 'S0029', 'S0030', 'S0031',
298
+ 'S0091', 'S0092', 'S0095', 'S0096', 'S0097',
299
+ 'S0205', 'S0206', 'S0207', 'S0208'
300
+ return send("handle_#{code.downcase}", code, name)
301
+ else
302
+ raise InvalidMessage.new "unknown status code #{code}"
303
+ end
304
+ end
305
+
306
+ def handle_s0001 status_code, status_name=nil
307
+ case status_name
308
+ when 'signalgroupstatus'
309
+ TrafficControllerSite.make_status format_signal_group_status
310
+ when 'cyclecounter'
311
+ TrafficControllerSite.make_status @pos.to_s
312
+ when 'basecyclecounter'
313
+ TrafficControllerSite.make_status @pos.to_s
314
+ when 'stage'
315
+ TrafficControllerSite.make_status 0.to_s
316
+ end
317
+ end
318
+
319
+ def handle_s0002 status_code, status_name=nil
320
+ case status_name
321
+ when 'detectorlogicstatus'
322
+ TrafficControllerSite.make_status @detector_logics.map { |dl| dl.value ? '1' : '0' }.join
323
+ end
324
+ end
325
+
326
+ def handle_s0003 status_code, status_name=nil
327
+ case status_name
328
+ when 'inputstatus'
329
+ TrafficControllerSite.make_status @input_results
330
+ when 'extendedinputstatus'
331
+ TrafficControllerSite.make_status 0.to_s
332
+ end
333
+ end
334
+
335
+ def handle_s0004 status_code, status_name=nil
336
+ case status_name
337
+ when 'outputstatus'
338
+ TrafficControllerSite.make_status 0
339
+ when 'extendedoutputstatus'
340
+ TrafficControllerSite.make_status 0
341
+ end
342
+ end
343
+
344
+ def handle_s0005 status_code, status_name=nil
345
+ case status_name
346
+ when 'status'
347
+ TrafficControllerSite.make_status @is_starting
348
+ end
349
+ end
350
+
351
+ def handle_s0006 status_code, status_name=nil
352
+ case status_name
353
+ when 'status'
354
+ TrafficControllerSite.make_status @emergency_route
355
+ when 'emergencystage'
356
+ TrafficControllerSite.make_status @emergency_route_number
357
+ end
358
+ end
359
+
360
+ def handle_s0007 status_code, status_name=nil
361
+ case status_name
362
+ when 'intersection'
363
+ TrafficControllerSite.make_status @intersection
364
+ when 'status'
365
+ TrafficControllerSite.make_status !@dark_mode
366
+ end
367
+ end
368
+
369
+ def handle_s0008 status_code, status_name=nil
370
+ case status_name
371
+ when 'intersection'
372
+ TrafficControllerSite.make_status @intersection
373
+ when 'status'
374
+ TrafficControllerSite.make_status @manual_control
375
+ end
376
+ end
377
+
378
+ def handle_s0009 status_code, status_name=nil
379
+ case status_name
380
+ when 'intersection'
381
+ TrafficControllerSite.make_status @intersection
382
+ when 'status'
383
+ TrafficControllerSite.make_status @fixed_time_control
384
+ end
385
+ end
386
+
387
+ def handle_s0010 status_code, status_name=nil
388
+ case status_name
389
+ when 'intersection'
390
+ TrafficControllerSite.make_status @intersection
391
+ when 'status'
392
+ TrafficControllerSite.make_status @isolated_control
393
+ end
394
+ end
395
+
396
+ def handle_s0011 status_code, status_name=nil
397
+ case status_name
398
+ when 'intersection'
399
+ TrafficControllerSite.make_status @intersection
400
+ when 'status'
401
+ TrafficControllerSite.make_status @yellow_flash
402
+ end
403
+ end
404
+
405
+ def handle_s0012 status_code, status_name=nil
406
+ case status_name
407
+ when 'intersection'
408
+ TrafficControllerSite.make_status @intersection
409
+ when 'status'
410
+ TrafficControllerSite.make_status @all_red
411
+ end
412
+ end
413
+
414
+ def handle_s0013 status_code, status_name=nil
415
+ case status_name
416
+ when 'intersection'
417
+ TrafficControllerSite.make_status @intersection
418
+ when 'status'
419
+ TrafficControllerSite.make_status @police_key
420
+ end
421
+ end
422
+
423
+ def handle_s0014 status_code, status_name=nil
424
+ case status_name
425
+ when 'status'
426
+ TrafficControllerSite.make_status @plan
427
+ end
428
+ end
429
+
430
+ def handle_s0015 status_code, status_name=nil
431
+ case status_name
432
+ when 'status'
433
+ TrafficControllerSite.make_status @traffic_situation
434
+ end
435
+ end
436
+
437
+ def handle_s0016 status_code, status_name=nil
438
+ case status_name
439
+ when 'number'
440
+ TrafficControllerSite.make_status @detector_logics.size
441
+ end
442
+ end
443
+
444
+ def handle_s0017 status_code, status_name=nil
445
+ case status_name
446
+ when 'number'
447
+ TrafficControllerSite.make_status @signal_groups.size
448
+ end
449
+ end
450
+
451
+ def handle_s0018 status_code, status_name=nil
452
+ case status_name
453
+ when 'number'
454
+ TrafficControllerSite.make_status @plans.size
455
+ end
456
+ end
457
+
458
+ def handle_s0019 status_code, status_name=nil
459
+ case status_name
460
+ when 'number'
461
+ TrafficControllerSite.make_status @num_traffic_situations
462
+ end
463
+ end
464
+
465
+ def handle_s0020 status_code, status_name=nil
466
+ case status_name
467
+ when 'intersection'
468
+ TrafficControllerSite.make_status @intersection
469
+ when 'controlmode'
470
+ TrafficControllerSite.make_status @control_mode
471
+ end
472
+ end
473
+
474
+ def handle_s0021 status_code, status_name=nil
475
+ case status_name
476
+ when 'detectorlogics'
477
+ TrafficControllerSite.make_status @detector_logics.map { |logic| logic.forced=='True' ? '1' : '0'}.join
478
+ end
479
+ end
480
+
481
+ def handle_s0022 status_code, status_name=nil
482
+ case status_name
483
+ when 'status'
484
+ TrafficControllerSite.make_status @plans.keys.join(',')
485
+ end
486
+ end
487
+
488
+ def handle_s0023 status_code, status_name=nil
489
+ case status_name
490
+ when 'status'
491
+ dynamic_bands = @plans.map { |nr,plan| plan.dynamic_bands_string }
492
+ str = dynamic_bands.compact.join(',')
493
+ TrafficControllerSite.make_status str
494
+ end
495
+ end
496
+
497
+ def handle_s0024 status_code, status_name=nil
498
+ case status_name
499
+ when 'status'
500
+ TrafficControllerSite.make_status '1-0'
501
+ end
502
+ end
503
+
504
+ def handle_s0026 status_code, status_name=nil
505
+ case status_name
506
+ when 'status'
507
+ TrafficControllerSite.make_status '0-00'
508
+ end
509
+ end
510
+
511
+ def handle_s0027 status_code, status_name=nil
512
+ case status_name
513
+ when 'status'
514
+ TrafficControllerSite.make_status '00-00-00-00'
515
+ end
516
+ end
517
+
518
+ def handle_s0028 status_code, status_name=nil
519
+ case status_name
520
+ when 'status'
521
+ TrafficControllerSite.make_status '00-00'
522
+ end
523
+ end
524
+
525
+ def handle_s0029 status_code, status_name=nil
526
+ case status_name
527
+ when 'status'
528
+ TrafficControllerSite.make_status ''
529
+ end
530
+ end
531
+
532
+ def handle_s0030 status_code, status_name=nil
533
+ case status_name
534
+ when 'status'
535
+ TrafficControllerSite.make_status ''
536
+ end
537
+ end
538
+
539
+ def handle_s0031 status_code, status_name=nil
540
+ case status_name
541
+ when 'status'
542
+ TrafficControllerSite.make_status ''
543
+ end
544
+ end
545
+
546
+ def handle_s0091 status_code, status_name=nil
547
+ case status_name
548
+ when 'user'
549
+ TrafficControllerSite.make_status 'nobody'
550
+ when 'status'
551
+ TrafficControllerSite.make_status 'logout'
552
+ end
553
+ end
554
+
555
+ def handle_s0092 status_code, status_name=nil
556
+ case status_name
557
+ when 'user'
558
+ TrafficControllerSite.make_status 'nobody'
559
+ when 'status'
560
+ TrafficControllerSite.make_status 'logout'
561
+ end
562
+ end
563
+
564
+ def handle_s0095 status_code, status_name=nil
565
+ case status_name
566
+ when 'status'
567
+ TrafficControllerSite.make_status RSMP::VERSION
568
+ end
569
+ end
570
+
571
+ def handle_s0096 status_code, status_name=nil
572
+ now = clock.now
573
+ case status_name
574
+ when 'year'
575
+ TrafficControllerSite.make_status now.year.to_s.rjust(4, "0")
576
+ when 'month'
577
+ TrafficControllerSite.make_status now.month.to_s.rjust(2, "0")
578
+ when 'day'
579
+ TrafficControllerSite.make_status now.day.to_s.rjust(2, "0")
580
+ when 'hour'
581
+ TrafficControllerSite.make_status now.hour.to_s.rjust(2, "0")
582
+ when 'minute'
583
+ TrafficControllerSite.make_status now.min.to_s.rjust(2, "0")
584
+ when 'second'
585
+ TrafficControllerSite.make_status now.sec.to_s.rjust(2, "0")
586
+ end
587
+ end
588
+
589
+ def handle_s0097 status_code, status_name=nil
590
+ case status_name
591
+ when 'checksum'
592
+ TrafficControllerSite.make_status '1'
593
+ when 'timestamp'
594
+ now = @node.clock.to_s
595
+ TrafficControllerSite.make_status now
596
+ end
597
+ end
598
+
599
+ def handle_s0205 status_code, status_name=nil
600
+ case status_name
601
+ when 'start'
602
+ TrafficControllerSite.make_status clock.to_s
603
+ when 'vehicles'
604
+ TrafficControllerSite.make_status 0
605
+ end
606
+ end
607
+
608
+ def handle_s0206 status_code, status_name=nil
609
+ case status_name
610
+ when 'start'
611
+ TrafficControllerSite.make_status clock.to_s
612
+ when 'speed'
613
+ TrafficControllerSite.make_status 0
614
+ end
615
+ end
616
+
617
+ def handle_s0207 status_code, status_name=nil
618
+ case status_name
619
+ when 'start'
620
+ TrafficControllerSite.make_status clock.to_s
621
+ when 'occupancy'
622
+ TrafficControllerSite.make_status 0
623
+ end
624
+ end
625
+
626
+ def handle_s0208 status_code, status_name=nil
627
+ case status_name
628
+ when 'start'
629
+ TrafficControllerSite.make_status clock.to_s
630
+ when 'P'
631
+ TrafficControllerSite.make_status 0
632
+ when 'PS'
633
+ TrafficControllerSite.make_status 0
634
+ when 'L'
635
+ TrafficControllerSite.make_status 0
636
+ when 'LS'
637
+ TrafficControllerSite.make_status 0
638
+ when 'B'
639
+ TrafficControllerSite.make_status 0
640
+ when 'SP'
641
+ TrafficControllerSite.make_status 0
642
+ when 'MC'
643
+ TrafficControllerSite.make_status 0
644
+ when 'C'
645
+ TrafficControllerSite.make_status 0
646
+ when 'F'
647
+ TrafficControllerSite.make_status 0
648
+ end
649
+ end
650
+ end
651
+ end
652
+ end