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.
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