rsmp 0.1.17 → 0.1.30

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