rsmp 0.1.12 → 0.1.27

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