rsmp 0.1.17 → 0.1.30

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rsmp/tlc.rb CHANGED
@@ -4,13 +4,20 @@ 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
10
  @signal_groups = []
10
11
  @detector_logics = []
11
12
  @plans = []
12
- @pos = 0
13
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
14
21
  @plan = 0
15
22
  @dark_mode = false
16
23
  @yellow_flash = false
@@ -22,19 +29,22 @@ module RSMP
22
29
  @emergency_route = false
23
30
  @emergency_route_number = 0
24
31
  @traffic_situation = 0
25
- @num_traffic_situations = 1
26
32
  @manual_control = false
27
33
  @fixed_time_control = false
28
34
  @isolated_control = false
29
35
  @yellow_flash = false
30
36
  @all_red = false
31
37
 
32
- @num_inputs = 8
33
38
  @inputs = '0'*@num_inputs
34
39
  @input_activations = '0'*@num_inputs
35
40
  @input_results = '0'*@num_inputs
36
41
  end
37
42
 
43
+ def clock
44
+ node.clock
45
+ end
46
+
47
+
38
48
  def add_signal_group group
39
49
  @signal_groups << group
40
50
  end
@@ -42,8 +52,12 @@ module RSMP
42
52
  def add_detector_logic logic
43
53
  @detector_logics << logic
44
54
  end
55
+
45
56
  def timer now
46
- 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
47
61
  if pos != @pos
48
62
  @pos = pos
49
63
  move pos
@@ -74,37 +88,33 @@ module RSMP
74
88
  end
75
89
 
76
90
  def format_signal_group_status
77
- @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
78
96
  end
79
97
 
80
98
  def handle_command command_code, arg
81
99
  case command_code
82
- when 'M0001'
83
- handle_m0001 arg
84
- when 'M0002'
85
- handle_m0002 arg
86
- when 'M0003'
87
- handle_m0003 arg
88
- when 'M0004'
89
- handle_m0004 arg
90
- when 'M0005'
91
- handle_m0005 arg
92
- when 'M0006'
93
- handle_m0006 arg
94
- when 'M0007'
95
- 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)
96
106
  else
97
107
  raise UnknownCommand.new "Unknown command #{command_code}"
98
108
  end
99
109
  end
100
110
 
101
111
  def handle_m0001 arg
102
- @node.verify_security_code arg['securityCode']
112
+ @node.verify_security_code 2, arg['securityCode']
103
113
  switch_mode arg['status']
104
114
  end
105
115
 
106
116
  def handle_m0002 arg
107
- @node.verify_security_code arg['securityCode']
117
+ @node.verify_security_code 2, arg['securityCode']
108
118
  if RSMP::Tlc.from_rsmp_bool(arg['status'])
109
119
  switch_plan arg['timeplan']
110
120
  else
@@ -113,32 +123,108 @@ module RSMP
113
123
  end
114
124
 
115
125
  def handle_m0003 arg
116
- @node.verify_security_code arg['securityCode']
126
+ @node.verify_security_code 2, arg['securityCode']
117
127
  @traffic_situation = arg['traficsituation'].to_i
118
128
  end
119
129
 
120
130
  def handle_m0004 arg
121
- @node.verify_security_code arg['securityCode']
122
- # restarting the node means we will disconnect and reconnect.
123
- # note that this will happen immediately, and no
124
- # command response will therefore be sent
125
- log "Restarting TLC", level: :info
126
- @node.restart
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
127
136
  end
128
137
 
129
138
  def handle_m0005 arg
130
- @node.verify_security_code arg['securityCode']
131
- @emergency_route = arg['status'] == 'True'
139
+ @node.verify_security_code 2, arg['securityCode']
140
+ @emergency_route = (arg['status'] == 'True')
132
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
133
148
  end
134
149
 
135
150
  def handle_m0006 arg
136
- @node.verify_security_code arg['securityCode']
151
+ @node.verify_security_code 2, arg['securityCode']
137
152
  input = arg['input'].to_i
138
- return unless input>=0 && input<@num_inputs
139
- @input_activations[input] = (arg['status']=='True' ? '1' : '0')
140
- result = @input_activations[input]=='1' || @inputs[input]=='1'
141
- @input_results[input] = (result ? '1' : '0')
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
142
228
  end
143
229
 
144
230
  def set_input i, value
@@ -146,18 +232,18 @@ module RSMP
146
232
  @inputs[i] = (arg['value'] ? '1' : '0')
147
233
  end
148
234
 
149
- def handle_m0007 arg
150
- @node.verify_security_code arg['securityCode']
151
- set_fixed_time_control arg['status']
152
- end
153
-
154
235
  def set_fixed_time_control status
155
236
  @fixed_time_control = status
156
237
  end
157
238
 
158
239
  def switch_plan plan
159
- log "Switching to plan #{plan}", level: :info
160
- @plan = plan.to_i
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
161
247
  end
162
248
 
163
249
  def switch_mode mode
@@ -182,8 +268,8 @@ module RSMP
182
268
  'S0008', 'S0009', 'S0010', 'S0011', 'S0012', 'S0013', 'S0014',
183
269
  'S0015', 'S0016', 'S0017', 'S0018', 'S0019', 'S0020', 'S0021',
184
270
  'S0022', 'S0023', 'S0024', 'S0026', 'S0027', 'S0028',
185
- 'S0029',
186
- 'S0091', 'S0092', 'S0095', 'S0096',
271
+ 'S0029', 'S0030', 'S0031',
272
+ 'S0091', 'S0092', 'S0095', 'S0096', 'S0097',
187
273
  'S0205', 'S0206', 'S0207', 'S0208'
188
274
  return send("handle_#{code.downcase}", code, name)
189
275
  else
@@ -194,7 +280,7 @@ module RSMP
194
280
  def handle_s0001 status_code, status_name=nil
195
281
  case status_name
196
282
  when 'signalgroupstatus'
197
- return RSMP::Tlc.make_status format_signal_group_status
283
+ RSMP::Tlc.make_status format_signal_group_status
198
284
  when 'cyclecounter'
199
285
  RSMP::Tlc.make_status @pos.to_s
200
286
  when 'basecyclecounter'
@@ -415,6 +501,20 @@ module RSMP
415
501
  end
416
502
  end
417
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
+
418
518
  def handle_s0091 status_code, status_name=nil
419
519
  case status_name
420
520
  when 'user'
@@ -441,26 +541,37 @@ module RSMP
441
541
  end
442
542
 
443
543
  def handle_s0096 status_code, status_name=nil
544
+ now = clock.now
444
545
  case status_name
445
546
  when 'year'
446
- RSMP::Tlc.make_status RSMP.now_object.year.to_s.rjust(4, "0")
547
+ RSMP::Tlc.make_status now.year.to_s.rjust(4, "0")
447
548
  when 'month'
448
- RSMP::Tlc.make_status RSMP.now_object.month.to_s.rjust(2, "0")
549
+ RSMP::Tlc.make_status now.month.to_s.rjust(2, "0")
449
550
  when 'day'
450
- RSMP::Tlc.make_status RSMP.now_object.day.to_s.rjust(2, "0")
551
+ RSMP::Tlc.make_status now.day.to_s.rjust(2, "0")
451
552
  when 'hour'
452
- RSMP::Tlc.make_status RSMP.now_object.hour.to_s.rjust(2, "0")
553
+ RSMP::Tlc.make_status now.hour.to_s.rjust(2, "0")
453
554
  when 'minute'
454
- RSMP::Tlc.make_status RSMP.now_object.min.to_s.rjust(2, "0")
555
+ RSMP::Tlc.make_status now.min.to_s.rjust(2, "0")
455
556
  when 'second'
456
- RSMP::Tlc.make_status RSMP.now_object.sec.to_s.rjust(2, "0")
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
457
568
  end
458
569
  end
459
570
 
460
571
  def handle_s0205 status_code, status_name=nil
461
572
  case status_name
462
573
  when 'start'
463
- RSMP::Tlc.make_status RSMP.now_string
574
+ RSMP::Tlc.make_status clock.to_s
464
575
  when 'vehicles'
465
576
  RSMP::Tlc.make_status 0
466
577
  end
@@ -469,7 +580,7 @@ module RSMP
469
580
  def handle_s0206 status_code, status_name=nil
470
581
  case status_name
471
582
  when 'start'
472
- RSMP::Tlc.make_status RSMP.now_string
583
+ RSMP::Tlc.make_status clock.to_s
473
584
  when 'speed'
474
585
  RSMP::Tlc.make_status 0
475
586
  end
@@ -478,7 +589,7 @@ module RSMP
478
589
  def handle_s0207 status_code, status_name=nil
479
590
  case status_name
480
591
  when 'start'
481
- RSMP::Tlc.make_status RSMP.now_string
592
+ RSMP::Tlc.make_status clock.to_s
482
593
  when 'occupancy'
483
594
  RSMP::Tlc.make_status 0
484
595
  end
@@ -487,7 +598,7 @@ module RSMP
487
598
  def handle_s0208 status_code, status_name=nil
488
599
  case status_name
489
600
  when 'start'
490
- RSMP::Tlc.make_status RSMP.now_string
601
+ RSMP::Tlc.make_status clock.to_s
491
602
  when 'P'
492
603
  RSMP::Tlc.make_status 0
493
604
  when 'PS'
@@ -532,6 +643,31 @@ module RSMP
532
643
  @state = get_state pos
533
644
  end
534
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
+
535
671
  def get_status code, name=nil
536
672
  case code
537
673
  when 'S0025'
@@ -542,21 +678,22 @@ module RSMP
542
678
  end
543
679
 
544
680
  def handle_s0025 status_code, status_name=nil
681
+ now = @node.clock.to_s
545
682
  case status_name
546
683
  when 'minToGEstimate'
547
- RSMP::Tlc.make_status RSMP.now_string
684
+ RSMP::Tlc.make_status now
548
685
  when 'maxToGEstimate'
549
- RSMP::Tlc.make_status RSMP.now_string
686
+ RSMP::Tlc.make_status now
550
687
  when 'likelyToGEstimate'
551
- RSMP::Tlc.make_status RSMP.now_string
688
+ RSMP::Tlc.make_status now
552
689
  when 'ToGConfidence'
553
690
  RSMP::Tlc.make_status 0
554
691
  when 'minToREstimate'
555
- RSMP::Tlc.make_status RSMP.now_string
692
+ RSMP::Tlc.make_status now
556
693
  when 'maxToREstimate'
557
- RSMP::Tlc.make_status RSMP.now_string
694
+ RSMP::Tlc.make_status now
558
695
  when 'likelyToREstimate'
559
- RSMP::Tlc.make_status RSMP.now_string
696
+ RSMP::Tlc.make_status now
560
697
  when 'ToRConfidence'
561
698
  RSMP::Tlc.make_status 0
562
699
  end
@@ -584,7 +721,7 @@ module RSMP
584
721
  def handle_s0201 status_code, status_name=nil
585
722
  case status_name
586
723
  when 'starttime'
587
- RSMP::Tlc.make_status RSMP.now_string
724
+ RSMP::Tlc.make_status @node.clock.to_s
588
725
  when 'vehicles'
589
726
  RSMP::Tlc.make_status 0
590
727
  end
@@ -593,7 +730,7 @@ module RSMP
593
730
  def handle_s0202 status_code, status_name=nil
594
731
  case status_name
595
732
  when 'starttime'
596
- RSMP::Tlc.make_status RSMP.now_string
733
+ RSMP::Tlc.make_status @node.clock.to_s
597
734
  when 'speed'
598
735
  RSMP::Tlc.make_status 0
599
736
  end
@@ -602,7 +739,7 @@ module RSMP
602
739
  def handle_s0203 status_code, status_name=nil
603
740
  case status_name
604
741
  when 'starttime'
605
- RSMP::Tlc.make_status RSMP.now_string
742
+ RSMP::Tlc.make_status @node.clock.to_s
606
743
  when 'occupancy'
607
744
  RSMP::Tlc.make_status 0
608
745
  end
@@ -611,7 +748,7 @@ module RSMP
611
748
  def handle_s0204 status_code, status_name=nil
612
749
  case status_name
613
750
  when 'starttime'
614
- RSMP::Tlc.make_status RSMP.now_string
751
+ RSMP::Tlc.make_status @node.clock.to_s
615
752
  when 'P'
616
753
  RSMP::Tlc.make_status 0
617
754
  when 'PS'
@@ -643,7 +780,7 @@ module RSMP
643
780
  end
644
781
 
645
782
  def handle_m0008 arg
646
- @node.verify_security_code arg['securityCode']
783
+ @node.verify_security_code 2, arg['securityCode']
647
784
  force_detector_logic arg['status']=='True', arg['value']='True'
648
785
  arg
649
786
  end
@@ -656,11 +793,12 @@ module RSMP
656
793
  end
657
794
 
658
795
  class Tlc < Site
796
+ attr_accessor :main
659
797
  def initialize options={}
660
798
  super options
661
-
662
799
  @sxl = 'traffic_light_controller'
663
-
800
+ @security_codes = options[:site_settings]['security_codes']
801
+ @interval = options[:site_settings]['interval'] || 1
664
802
  unless @main
665
803
  raise ConfigurationError.new "TLC must have a main component"
666
804
  end
@@ -687,31 +825,38 @@ module RSMP
687
825
  end
688
826
 
689
827
  def start_timer
690
- name = "tlc timer"
691
- interval = 1 #@settings["timer_interval"] || 1
692
- 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
693
830
 
694
831
  @timer = @task.async do |task|
695
- task.annotate "timer"
832
+ task.annotate task_name
696
833
  next_time = Time.now.to_f
697
834
  loop do
698
- now = RSMP.now_object
699
- break if timer(now) == false
700
- rescue StandardError => e
701
- log ["#{name} exception: #{e}",e.backtrace].flatten.join("\n"), level: :error
702
- ensure
703
- # adjust sleep duration to avoid drift. so wake up always happens on the
704
- # same fractional second.
705
- # note that Time.now is not monotonic. If the clock si changed,
706
- # either manaully or via NTP, the sleep interval might jump.
707
- # an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
708
- # to get the current time. this ensures a constant interval, but
709
- # if the clock is changed, the wake up would then happen on a different
710
- # fractional second
711
-
712
- next_time += interval
713
- duration = next_time - Time.now.to_f
714
- 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
715
860
  end
716
861
  end
717
862
  end
@@ -721,7 +866,16 @@ module RSMP
721
866
  @main.timer now
722
867
  end
723
868
 
724
- def verify_security_code code
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
874
+ end
875
+
876
+ def change_security_code level, old_code, new_code
877
+ verify_security_code level, old_code
878
+ @security_codes[level] = new_code
725
879
  end
726
880
 
727
881
  def self.to_rmsp_bool bool
@@ -745,5 +899,13 @@ module RSMP
745
899
  end
746
900
  end
747
901
 
902
+ def do_deferred item
903
+ case item
904
+ when :restart
905
+ log "Restarting TLC", level: :info
906
+ restart
907
+ end
908
+ end
909
+
748
910
  end
749
911
  end