rsmp 0.1.13 → 0.1.29

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rsmp/tlc.rb CHANGED
@@ -4,24 +4,60 @@ module RSMP
4
4
 
5
5
  class TrafficController < Component
6
6
  attr_reader :pos, :cycle_time
7
+
7
8
  def initialize node:, id:, cycle_time:
8
9
  super node: node, id: id, grouped: true
9
- @pos = 0
10
- @cycle_time = cycle_time
11
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
12
21
  @plan = 0
13
- @dark_mode = 'False'
14
- @yellow_flash = 'False'
15
- @booting = 'False'
22
+ @dark_mode = false
23
+ @yellow_flash = false
24
+ @booting = false
25
+ @control_mode = 'control'
16
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
17
45
  end
18
46
 
47
+
19
48
  def add_signal_group group
20
49
  @signal_groups << group
21
50
  end
22
51
 
52
+ def add_detector_logic logic
53
+ @detector_logics << logic
54
+ end
55
+
23
56
  def timer now
24
- pos = now.to_i % @cycle_time
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
25
61
  if pos != @pos
26
62
  @pos = pos
27
63
  move pos
@@ -32,108 +68,556 @@ module RSMP
32
68
  @signal_groups.each do |group|
33
69
  group.move pos
34
70
  end
35
- output_states
36
-
37
71
  if pos == 0
38
72
  aggrated_status_changed
39
73
  end
40
74
  end
41
75
 
42
76
  def output_states
43
- #print "\t#{pos.to_s.ljust(3)} #{format_signal_group_status}\r"
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"
44
88
  end
45
89
 
46
90
  def format_signal_group_status
47
- @signal_groups.map { |group|group.state }.join
91
+ if @yellow_flash
92
+ 'c' * @signal_groups.size
93
+ else
94
+ @signal_groups.map { |group| group.state }.join
95
+ end
48
96
  end
49
97
 
50
98
  def handle_command command_code, arg
51
99
  case command_code
52
- when 'M0001'
53
- handle_m0001 arg
54
- when 'M0002'
55
- handle_m0002 arg
56
- when 'M0007'
57
- handle_m0007 arg
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)
58
106
  else
59
107
  raise UnknownCommand.new "Unknown command #{command_code}"
60
108
  end
61
109
  end
62
110
 
63
111
  def handle_m0001 arg
64
- verify_security_code arg['securityCode']
112
+ @node.verify_security_code 2, arg['securityCode']
65
113
  switch_mode arg['status']
66
- arg
67
114
  end
68
115
 
69
116
  def handle_m0002 arg
70
- verify_security_code arg['securityCode']
71
- if arg['status'] = 'True'
117
+ @node.verify_security_code 2, arg['securityCode']
118
+ if RSMP::Tlc.from_rsmp_bool(arg['status'])
72
119
  switch_plan arg['timeplan']
73
120
  else
74
121
  switch_plan 0 # TODO use clock/calender
75
122
  end
76
- arg
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
77
163
  end
78
164
 
79
165
  def handle_m0007 arg
80
- verify_security_code arg['securityCode']
166
+ @node.verify_security_code 2, arg['securityCode']
81
167
  set_fixed_time_control arg['status']
82
- arg
83
168
  end
84
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
+
85
235
  def set_fixed_time_control status
86
236
  @fixed_time_control = status
87
237
  end
88
238
 
89
239
  def switch_plan plan
90
- log "Switching to plan #{plan}", level: :info
91
- @plan = plan.to_i
92
- 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
93
247
  end
94
248
 
95
- def switch_mode mode
249
+ def switch_mode mode
96
250
  log "Switching to mode #{mode}", level: :info
97
251
  case mode
98
252
  when 'NormalControl'
99
- @yellow_flash = 'False'
100
- @dark_mode = 'False'
253
+ @yellow_flash = false
254
+ @dark_mode = false
101
255
  when 'YellowFlash'
102
- @yellow_flash = 'True'
103
- @dark_mode = 'False'
256
+ @yellow_flash = true
257
+ @dark_mode = false
104
258
  when 'Dark'
105
- @yellow_flash = 'False'
106
- @dark_mode = 'True'
259
+ @yellow_flash = false
260
+ @dark_mode = true
107
261
  end
108
262
  mode
109
263
  end
110
264
 
111
- def get_status status_code, status_name=nil
112
- case status_code
113
- when 'S0001'
114
- return format_signal_group_status, "recent"
115
- when 'S0005'
116
- return @booting, "recent"
117
- when 'S0007'
118
- if @dark_mode == 'True'
119
- return 'False', "recent"
120
- else
121
- return 'True', "recent"
122
- end
123
- when 'S0009'
124
- return @fixed_time_control.to_s, "recent"
125
- when 'S0013'
126
- return @police_key.to_s, "recent"
127
- when 'S0014'
128
- return @plan.to_s, "recent"
129
- when 'S0011'
130
- return @yellow_flash, "recent"
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)
131
275
  else
132
- raise InvalidMessage.new "unknown status code #{status_code}"
276
+ raise InvalidMessage.new "unknown status code #{code}"
133
277
  end
134
278
  end
135
279
 
136
- def verify_security_code code
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.forced ? '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
137
621
  end
138
622
 
139
623
  end
@@ -158,27 +642,180 @@ module RSMP
158
642
  def move pos
159
643
  @state = get_state pos
160
644
  end
645
+
646
+ def handle_command command_code, arg
647
+ case command_code
648
+ when 'M0010', 'M0011'
649
+ return send("handle_#{command_code.downcase}", arg)
650
+ else
651
+ raise UnknownCommand.new "Unknown command #{command_code}"
652
+ end
653
+ end
654
+
655
+ # Start of signal group. Orders a signal group to green
656
+ def handle_m0010 arg
657
+ @node.verify_security_code 2, arg['securityCode']
658
+ if RSMP::Tlc.from_rsmp_bool arg['status']
659
+ log "Start signal group #{c_id}, go to green", level: :info
660
+ end
661
+ end
662
+
663
+ # Stop of signal group. Orders a signal group to red
664
+ def handle_m0011 arg
665
+ @node.verify_security_code 2, arg['securityCode']
666
+ if RSMP::Tlc.from_rsmp_bool arg['status']
667
+ log "Stop signal group #{c_id}, go to red", level: :info
668
+ end
669
+ end
670
+
671
+ def get_status code, name=nil
672
+ case code
673
+ when 'S0025'
674
+ return send("handle_#{code.downcase}", code, name)
675
+ else
676
+ raise InvalidMessage.new "unknown status code #{code}"
677
+ end
678
+ end
679
+
680
+ def handle_s0025 status_code, status_name=nil
681
+ now = @node.clock.to_s
682
+ case status_name
683
+ when 'minToGEstimate'
684
+ RSMP::Tlc.make_status now
685
+ when 'maxToGEstimate'
686
+ RSMP::Tlc.make_status now
687
+ when 'likelyToGEstimate'
688
+ RSMP::Tlc.make_status now
689
+ when 'ToGConfidence'
690
+ RSMP::Tlc.make_status 0
691
+ when 'minToREstimate'
692
+ RSMP::Tlc.make_status now
693
+ when 'maxToREstimate'
694
+ RSMP::Tlc.make_status now
695
+ when 'likelyToREstimate'
696
+ RSMP::Tlc.make_status now
697
+ when 'ToRConfidence'
698
+ RSMP::Tlc.make_status 0
699
+ end
700
+ end
701
+ end
702
+
703
+ class DetectorLogic < Component
704
+ attr_reader :status, :forced, :value
705
+
706
+ def initialize node:, id:
707
+ super node: node, id: id, grouped: false
708
+ @forced = 0
709
+ @value = 0
710
+ end
711
+
712
+ def get_status code, name=nil
713
+ case code
714
+ when 'S0201', 'S0202', 'S0203', 'S0204'
715
+ return send("handle_#{code.downcase}", code, name)
716
+ else
717
+ raise InvalidMessage.new "unknown status code #{code}"
718
+ end
719
+ end
720
+
721
+ def handle_s0201 status_code, status_name=nil
722
+ case status_name
723
+ when 'starttime'
724
+ RSMP::Tlc.make_status @node.clock.to_s
725
+ when 'vehicles'
726
+ RSMP::Tlc.make_status 0
727
+ end
728
+ end
729
+
730
+ def handle_s0202 status_code, status_name=nil
731
+ case status_name
732
+ when 'starttime'
733
+ RSMP::Tlc.make_status @node.clock.to_s
734
+ when 'speed'
735
+ RSMP::Tlc.make_status 0
736
+ end
737
+ end
738
+
739
+ def handle_s0203 status_code, status_name=nil
740
+ case status_name
741
+ when 'starttime'
742
+ RSMP::Tlc.make_status @node.clock.to_s
743
+ when 'occupancy'
744
+ RSMP::Tlc.make_status 0
745
+ end
746
+ end
747
+
748
+ def handle_s0204 status_code, status_name=nil
749
+ case status_name
750
+ when 'starttime'
751
+ RSMP::Tlc.make_status @node.clock.to_s
752
+ when 'P'
753
+ RSMP::Tlc.make_status 0
754
+ when 'PS'
755
+ RSMP::Tlc.make_status 0
756
+ when 'L'
757
+ RSMP::Tlc.make_status 0
758
+ when 'LS'
759
+ RSMP::Tlc.make_status 0
760
+ when 'B'
761
+ RSMP::Tlc.make_status 0
762
+ when 'SP'
763
+ RSMP::Tlc.make_status 0
764
+ when 'MC'
765
+ RSMP::Tlc.make_status 0
766
+ when 'C'
767
+ RSMP::Tlc.make_status 0
768
+ when 'F'
769
+ RSMP::Tlc.make_status 0
770
+ end
771
+ end
772
+
773
+ def handle_command command_code, arg
774
+ case command_code
775
+ when 'M0008'
776
+ handle_m0008 arg
777
+ else
778
+ raise UnknownCommand.new "Unknown command #{command_code}"
779
+ end
780
+ end
781
+
782
+ def handle_m0008 arg
783
+ @node.verify_security_code 2, arg['securityCode']
784
+ force_detector_logic arg['status']=='True', arg['value']='True'
785
+ arg
786
+ end
787
+
788
+ def force_detector_logic status, value
789
+ @forced = status
790
+ @value = value
791
+ end
792
+
161
793
  end
162
794
 
163
795
  class Tlc < Site
796
+ attr_accessor :main
164
797
  def initialize options={}
165
798
  super options
166
-
167
799
  @sxl = 'traffic_light_controller'
168
-
800
+ @security_codes = options[:site_settings]['security_codes']
801
+ @interval = options[:site_settings]['interval'] || 1
169
802
  unless @main
170
803
  raise ConfigurationError.new "TLC must have a main component"
171
804
  end
172
805
  end
173
806
 
174
- def build_component id, settings={}
175
- component = case settings['type']
807
+ def build_component id:, type:, settings:{}
808
+ component = case type
176
809
  when 'main'
177
810
  @main = TrafficController.new node: self, id: id, cycle_time: settings['cycle_time']
178
811
  when 'signal_group'
179
812
  group = SignalGroup.new node: self, id: id, plan: settings['plan']
180
813
  @main.add_signal_group group
181
814
  group
815
+ when 'detector_logic'
816
+ logic = DetectorLogic.new node: self, id: id
817
+ @main.add_detector_logic logic
818
+ logic
182
819
  end
183
820
  end
184
821
 
@@ -188,31 +825,38 @@ module RSMP
188
825
  end
189
826
 
190
827
  def start_timer
191
- name = "tlc timer"
192
- interval = 1 #@settings["timer_interval"] || 1
193
- log "Starting #{name} with interval #{interval} seconds", level: :debug
828
+ task_name = "tlc timer"
829
+ log "Starting #{task_name} with interval #{@interval} seconds", level: :debug
194
830
 
195
831
  @timer = @task.async do |task|
196
- task.annotate "timer"
832
+ task.annotate task_name
197
833
  next_time = Time.now.to_f
198
834
  loop do
199
- now = RSMP.now_object
200
- break if timer(now) == false
201
- rescue StandardError => e
202
- log ["#{name} exception: #{e}",e.backtrace].flatten.join("\n"), level: :error
203
- ensure
204
- # adjust sleep duration to avoid drift. so wake up always happens on the
205
- # same fractional second.
206
- # note that Time.now is not monotonic. If the clock si changed,
207
- # either manaully or via NTP, the sleep interval might jump.
208
- # an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
209
- # to get the current time. this ensures a constant interval, but
210
- # if the clock is changed, the wake up would then happen on a different
211
- # fractional second
212
-
213
- next_time += interval
214
- duration = next_time - Time.now.to_f
215
- task.sleep duration
835
+ begin
836
+ timer(@clock.now)
837
+ rescue EOFError => e
838
+ log "Connection closed: #{e}", level: :warning
839
+ rescue IOError => e
840
+ log "IOError", level: :warning
841
+ rescue Errno::ECONNRESET
842
+ log "Connection reset by peer", level: :warning
843
+ rescue Errno::EPIPE => e
844
+ log "Broken pipe", level: :warning
845
+ rescue StandardError => e
846
+ notify_error e, level: :internal
847
+ ensure
848
+ # adjust sleep duration to avoid drift. so wake up always happens on the
849
+ # same fractional second.
850
+ # note that Time.now is not monotonic. If the clock is changed,
851
+ # either manaully or via NTP, the sleep interval might jump.
852
+ # an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
853
+ # to get the current time. this ensures a constant interval, but
854
+ # if the clock is changed, the wake up would then happen on a different
855
+ # fractional second
856
+ next_time += @interval
857
+ duration = next_time - Time.now.to_f
858
+ task.sleep duration
859
+ end
216
860
  end
217
861
  end
218
862
  end
@@ -222,14 +866,45 @@ module RSMP
222
866
  @main.timer now
223
867
  end
224
868
 
225
- def handle_command command_code, arg
226
- return unless @main
227
- @main.handle_command command_code, arg
869
+ def verify_security_code level, code
870
+ raise ArgumentError.new("Level must be 1-2, got #{level}") unless (1..2).include?(level)
871
+ if @security_codes[level] != code
872
+ raise MessageRejected.new("Wrong security code for level #{level}")
873
+ end
228
874
  end
229
875
 
230
- def get_status status_code, status_name=nil
231
- return unless @main
232
- @main.get_status status_code, status_name
876
+ def change_security_code level, old_code, new_code
877
+ verify_security_code level, old_code
878
+ @security_codes[level] = new_code
879
+ end
880
+
881
+ def self.to_rmsp_bool bool
882
+ if bool
883
+ 'True'
884
+ else
885
+ 'False'
886
+ end
887
+ end
888
+
889
+ def self.from_rsmp_bool str
890
+ str == 'True'
891
+ end
892
+
893
+ def self.make_status value, q='recent'
894
+ case value
895
+ when true, false
896
+ return to_rmsp_bool(value), q
897
+ else
898
+ return value, q
899
+ end
900
+ end
901
+
902
+ def do_deferred item
903
+ case item
904
+ when :restart
905
+ log "Restarting TLC", level: :info
906
+ restart
907
+ end
233
908
  end
234
909
 
235
910
  end