rsmp 0.1.13 → 0.1.29

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