rsmp 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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