rsmp 0.1.19 → 0.1.31
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.
- checksums.yaml +4 -4
- data/.gitmodules +0 -4
- data/Gemfile.lock +16 -14
- data/config/supervisor.yaml +9 -15
- data/config/tlc.yaml +9 -8
- data/documentation/{classes.md → classes_and_modules.md} +25 -22
- data/documentation/message_distribution.md +23 -0
- data/lib/rsmp.rb +14 -5
- data/lib/rsmp/archive.rb +12 -17
- data/lib/rsmp/cli.rb +133 -102
- data/lib/rsmp/collector.rb +102 -0
- data/lib/rsmp/component.rb +2 -0
- data/lib/rsmp/{site_base.rb → components.rb} +3 -3
- data/lib/rsmp/convert/export/json_schema.rb +204 -0
- data/lib/rsmp/convert/import/yaml.rb +38 -0
- data/lib/rsmp/deep_merge.rb +11 -0
- data/lib/rsmp/inspect.rb +46 -0
- data/lib/rsmp/listener.rb +23 -0
- data/lib/rsmp/logger.rb +6 -4
- data/lib/rsmp/{base.rb → logging.rb} +3 -3
- data/lib/rsmp/message.rb +22 -31
- data/lib/rsmp/node.rb +15 -7
- data/lib/rsmp/notifier.rb +29 -0
- data/lib/rsmp/proxy.rb +114 -38
- data/lib/rsmp/rsmp.rb +34 -23
- data/lib/rsmp/site.rb +32 -34
- data/lib/rsmp/site_proxy.rb +160 -97
- data/lib/rsmp/site_proxy_wait.rb +206 -0
- data/lib/rsmp/supervisor.rb +82 -47
- data/lib/rsmp/supervisor_proxy.rb +21 -14
- data/lib/rsmp/tlc.rb +80 -38
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp/wait.rb +4 -151
- data/rsmp.gemspec +4 -15
- metadata +27 -124
- data/config/site.yaml +0 -40
- data/lib/rsmp/probe.rb +0 -101
- data/lib/rsmp/probe_collection.rb +0 -28
data/lib/rsmp/tlc.rb
CHANGED
@@ -13,7 +13,6 @@ module RSMP
|
|
13
13
|
@cycle_time = cycle_time
|
14
14
|
@num_traffic_situations = 1
|
15
15
|
@num_inputs = 8
|
16
|
-
|
17
16
|
reset
|
18
17
|
end
|
19
18
|
|
@@ -41,6 +40,11 @@ module RSMP
|
|
41
40
|
@input_results = '0'*@num_inputs
|
42
41
|
end
|
43
42
|
|
43
|
+
def clock
|
44
|
+
node.clock
|
45
|
+
end
|
46
|
+
|
47
|
+
|
44
48
|
def add_signal_group group
|
45
49
|
@signal_groups << group
|
46
50
|
end
|
@@ -48,8 +52,12 @@ module RSMP
|
|
48
52
|
def add_detector_logic logic
|
49
53
|
@detector_logics << logic
|
50
54
|
end
|
55
|
+
|
51
56
|
def timer now
|
52
|
-
|
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
|
53
61
|
if pos != @pos
|
54
62
|
@pos = pos
|
55
63
|
move pos
|
@@ -80,7 +88,11 @@ module RSMP
|
|
80
88
|
end
|
81
89
|
|
82
90
|
def format_signal_group_status
|
83
|
-
@
|
91
|
+
if @yellow_flash
|
92
|
+
'c' * @signal_groups.size
|
93
|
+
else
|
94
|
+
@signal_groups.map { |group| group.state }.join
|
95
|
+
end
|
84
96
|
end
|
85
97
|
|
86
98
|
def handle_command command_code, arg
|
@@ -125,18 +137,29 @@ module RSMP
|
|
125
137
|
|
126
138
|
def handle_m0005 arg
|
127
139
|
@node.verify_security_code 2, arg['securityCode']
|
128
|
-
@emergency_route = arg['status'] == 'True'
|
140
|
+
@emergency_route = (arg['status'] == 'True')
|
129
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
|
130
148
|
end
|
131
149
|
|
132
150
|
def handle_m0006 arg
|
133
151
|
@node.verify_security_code 2, arg['securityCode']
|
134
152
|
input = arg['input'].to_i
|
135
153
|
idx = input - 1
|
136
|
-
return unless idx>=0 && input<@num_inputs
|
154
|
+
return unless idx>=0 && input<@num_inputs # TODO should NotAck
|
137
155
|
@input_activations[idx] = (arg['status']=='True' ? '1' : '0')
|
138
156
|
result = @input_activations[idx]=='1' || @inputs[idx]=='1'
|
139
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
|
140
163
|
end
|
141
164
|
|
142
165
|
def handle_m0007 arg
|
@@ -191,6 +214,17 @@ module RSMP
|
|
191
214
|
|
192
215
|
def handle_m0104 arg
|
193
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
|
194
228
|
end
|
195
229
|
|
196
230
|
def set_input i, value
|
@@ -203,8 +237,13 @@ module RSMP
|
|
203
237
|
end
|
204
238
|
|
205
239
|
def switch_plan plan
|
206
|
-
|
207
|
-
|
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
|
208
247
|
end
|
209
248
|
|
210
249
|
def switch_mode mode
|
@@ -502,35 +541,37 @@ module RSMP
|
|
502
541
|
end
|
503
542
|
|
504
543
|
def handle_s0096 status_code, status_name=nil
|
544
|
+
now = clock.now
|
505
545
|
case status_name
|
506
546
|
when 'year'
|
507
|
-
RSMP::Tlc.make_status
|
547
|
+
RSMP::Tlc.make_status now.year.to_s.rjust(4, "0")
|
508
548
|
when 'month'
|
509
|
-
RSMP::Tlc.make_status
|
549
|
+
RSMP::Tlc.make_status now.month.to_s.rjust(2, "0")
|
510
550
|
when 'day'
|
511
|
-
RSMP::Tlc.make_status
|
551
|
+
RSMP::Tlc.make_status now.day.to_s.rjust(2, "0")
|
512
552
|
when 'hour'
|
513
|
-
RSMP::Tlc.make_status
|
553
|
+
RSMP::Tlc.make_status now.hour.to_s.rjust(2, "0")
|
514
554
|
when 'minute'
|
515
|
-
RSMP::Tlc.make_status
|
555
|
+
RSMP::Tlc.make_status now.min.to_s.rjust(2, "0")
|
516
556
|
when 'second'
|
517
|
-
RSMP::Tlc.make_status
|
557
|
+
RSMP::Tlc.make_status now.sec.to_s.rjust(2, "0")
|
518
558
|
end
|
519
559
|
end
|
520
560
|
|
521
561
|
def handle_s0097 status_code, status_name=nil
|
522
562
|
case status_name
|
523
|
-
when '
|
524
|
-
RSMP::Tlc.make_status '1'
|
525
|
-
when 'hash'
|
563
|
+
when 'checksum'
|
526
564
|
RSMP::Tlc.make_status '1'
|
565
|
+
when 'timestamp'
|
566
|
+
now = @node.clock.to_s
|
567
|
+
RSMP::Tlc.make_status now
|
527
568
|
end
|
528
569
|
end
|
529
570
|
|
530
571
|
def handle_s0205 status_code, status_name=nil
|
531
572
|
case status_name
|
532
573
|
when 'start'
|
533
|
-
RSMP::Tlc.make_status
|
574
|
+
RSMP::Tlc.make_status clock.to_s
|
534
575
|
when 'vehicles'
|
535
576
|
RSMP::Tlc.make_status 0
|
536
577
|
end
|
@@ -539,7 +580,7 @@ module RSMP
|
|
539
580
|
def handle_s0206 status_code, status_name=nil
|
540
581
|
case status_name
|
541
582
|
when 'start'
|
542
|
-
RSMP::Tlc.make_status
|
583
|
+
RSMP::Tlc.make_status clock.to_s
|
543
584
|
when 'speed'
|
544
585
|
RSMP::Tlc.make_status 0
|
545
586
|
end
|
@@ -548,7 +589,7 @@ module RSMP
|
|
548
589
|
def handle_s0207 status_code, status_name=nil
|
549
590
|
case status_name
|
550
591
|
when 'start'
|
551
|
-
RSMP::Tlc.make_status
|
592
|
+
RSMP::Tlc.make_status clock.to_s
|
552
593
|
when 'occupancy'
|
553
594
|
RSMP::Tlc.make_status 0
|
554
595
|
end
|
@@ -557,7 +598,7 @@ module RSMP
|
|
557
598
|
def handle_s0208 status_code, status_name=nil
|
558
599
|
case status_name
|
559
600
|
when 'start'
|
560
|
-
RSMP::Tlc.make_status
|
601
|
+
RSMP::Tlc.make_status clock.to_s
|
561
602
|
when 'P'
|
562
603
|
RSMP::Tlc.make_status 0
|
563
604
|
when 'PS'
|
@@ -637,21 +678,22 @@ module RSMP
|
|
637
678
|
end
|
638
679
|
|
639
680
|
def handle_s0025 status_code, status_name=nil
|
681
|
+
now = @node.clock.to_s
|
640
682
|
case status_name
|
641
683
|
when 'minToGEstimate'
|
642
|
-
RSMP::Tlc.make_status
|
684
|
+
RSMP::Tlc.make_status now
|
643
685
|
when 'maxToGEstimate'
|
644
|
-
RSMP::Tlc.make_status
|
686
|
+
RSMP::Tlc.make_status now
|
645
687
|
when 'likelyToGEstimate'
|
646
|
-
RSMP::Tlc.make_status
|
688
|
+
RSMP::Tlc.make_status now
|
647
689
|
when 'ToGConfidence'
|
648
690
|
RSMP::Tlc.make_status 0
|
649
691
|
when 'minToREstimate'
|
650
|
-
RSMP::Tlc.make_status
|
692
|
+
RSMP::Tlc.make_status now
|
651
693
|
when 'maxToREstimate'
|
652
|
-
RSMP::Tlc.make_status
|
694
|
+
RSMP::Tlc.make_status now
|
653
695
|
when 'likelyToREstimate'
|
654
|
-
RSMP::Tlc.make_status
|
696
|
+
RSMP::Tlc.make_status now
|
655
697
|
when 'ToRConfidence'
|
656
698
|
RSMP::Tlc.make_status 0
|
657
699
|
end
|
@@ -679,7 +721,7 @@ module RSMP
|
|
679
721
|
def handle_s0201 status_code, status_name=nil
|
680
722
|
case status_name
|
681
723
|
when 'starttime'
|
682
|
-
RSMP::Tlc.make_status
|
724
|
+
RSMP::Tlc.make_status @node.clock.to_s
|
683
725
|
when 'vehicles'
|
684
726
|
RSMP::Tlc.make_status 0
|
685
727
|
end
|
@@ -688,7 +730,7 @@ module RSMP
|
|
688
730
|
def handle_s0202 status_code, status_name=nil
|
689
731
|
case status_name
|
690
732
|
when 'starttime'
|
691
|
-
RSMP::Tlc.make_status
|
733
|
+
RSMP::Tlc.make_status @node.clock.to_s
|
692
734
|
when 'speed'
|
693
735
|
RSMP::Tlc.make_status 0
|
694
736
|
end
|
@@ -697,7 +739,7 @@ module RSMP
|
|
697
739
|
def handle_s0203 status_code, status_name=nil
|
698
740
|
case status_name
|
699
741
|
when 'starttime'
|
700
|
-
RSMP::Tlc.make_status
|
742
|
+
RSMP::Tlc.make_status @node.clock.to_s
|
701
743
|
when 'occupancy'
|
702
744
|
RSMP::Tlc.make_status 0
|
703
745
|
end
|
@@ -706,7 +748,7 @@ module RSMP
|
|
706
748
|
def handle_s0204 status_code, status_name=nil
|
707
749
|
case status_name
|
708
750
|
when 'starttime'
|
709
|
-
RSMP::Tlc.make_status
|
751
|
+
RSMP::Tlc.make_status @node.clock.to_s
|
710
752
|
when 'P'
|
711
753
|
RSMP::Tlc.make_status 0
|
712
754
|
when 'PS'
|
@@ -751,6 +793,7 @@ module RSMP
|
|
751
793
|
end
|
752
794
|
|
753
795
|
class Tlc < Site
|
796
|
+
attr_accessor :main
|
754
797
|
def initialize options={}
|
755
798
|
super options
|
756
799
|
@sxl = 'traffic_light_controller'
|
@@ -790,22 +833,21 @@ module RSMP
|
|
790
833
|
next_time = Time.now.to_f
|
791
834
|
loop do
|
792
835
|
begin
|
793
|
-
now
|
794
|
-
timer(now)
|
836
|
+
timer(@clock.now)
|
795
837
|
rescue EOFError => e
|
796
|
-
log "
|
838
|
+
log "Connection closed: #{e}", level: :warning
|
797
839
|
rescue IOError => e
|
798
|
-
log "
|
840
|
+
log "IOError", level: :warning
|
799
841
|
rescue Errno::ECONNRESET
|
800
|
-
log "
|
842
|
+
log "Connection reset by peer", level: :warning
|
801
843
|
rescue Errno::EPIPE => e
|
802
|
-
log "
|
844
|
+
log "Broken pipe", level: :warning
|
803
845
|
rescue StandardError => e
|
804
|
-
|
846
|
+
notify_error e, level: :internal
|
805
847
|
ensure
|
806
848
|
# adjust sleep duration to avoid drift. so wake up always happens on the
|
807
849
|
# same fractional second.
|
808
|
-
# note that Time.now is not monotonic. If the clock
|
850
|
+
# note that Time.now is not monotonic. If the clock is changed,
|
809
851
|
# either manaully or via NTP, the sleep interval might jump.
|
810
852
|
# an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
|
811
853
|
# to get the current time. this ensures a constant interval, but
|
data/lib/rsmp/version.rb
CHANGED
data/lib/rsmp/wait.rb
CHANGED
@@ -1,163 +1,16 @@
|
|
1
|
-
# Helper for waiting for an Async condition using a block
|
2
|
-
|
3
1
|
module RSMP
|
4
2
|
module Wait
|
5
|
-
|
3
|
+
# wait for an async condition to signal, then yield to block
|
4
|
+
# if block returns true we're done. otherwise, wait again
|
6
5
|
def wait_for condition, timeout, &block
|
7
|
-
raise RuntimeError.new("Can't wait for
|
6
|
+
raise RuntimeError.new("Can't wait for condition because task is not running") unless @task.running?
|
8
7
|
@task.with_timeout(timeout) do
|
9
|
-
while task.running? do
|
8
|
+
while @task.running? do
|
10
9
|
value = condition.wait
|
11
10
|
result = yield value
|
12
11
|
return result if result # return result of check, if not nil
|
13
12
|
end
|
14
13
|
end
|
15
14
|
end
|
16
|
-
|
17
|
-
def capture_status_updates_or_responses task, type, options, m_id
|
18
|
-
task.annotate "wait for status update/response"
|
19
|
-
want = convert_status_list options[:status_list]
|
20
|
-
result = {}
|
21
|
-
# wait for a status update
|
22
|
-
item = @archive.capture(task,options.merge({
|
23
|
-
type: [type,'MessageNotAck'],
|
24
|
-
num: 1
|
25
|
-
})) do |item|
|
26
|
-
message = item[:message]
|
27
|
-
if message.is_a?(MessageNotAck) && message.attribute('oMId') == m_id
|
28
|
-
# set result to an exception, but don't raise it.
|
29
|
-
# this will be returned by the task and stored as the task result
|
30
|
-
# when the parent task call wait() on the task, the exception
|
31
|
-
# will be raised in the parent task, and caught by rspec.
|
32
|
-
# rspec will then show the error and record the test as failed
|
33
|
-
m_id_short = RSMP::Message.shorten_m_id m_id, 8
|
34
|
-
result = RSMP::MessageRejected.new "Status request #{m_id_short} was rejected: #{message.attribute('rea')}"
|
35
|
-
next true # done, no more messages wanted
|
36
|
-
end
|
37
|
-
found = []
|
38
|
-
# look through querues
|
39
|
-
want.each_with_index do |query,i|
|
40
|
-
# look through status items in message
|
41
|
-
item[:message].attributes['sS'].each do |input|
|
42
|
-
ok = status_match? query, input
|
43
|
-
if ok
|
44
|
-
result[query] = input
|
45
|
-
found << i # record which queries where matched succesfully
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
# remove queries that where matched
|
50
|
-
found.sort.reverse.each do |i|
|
51
|
-
want.delete_at i
|
52
|
-
end
|
53
|
-
want.empty? # any queries left to match?
|
54
|
-
end
|
55
|
-
result
|
56
|
-
rescue Async::TimeoutError
|
57
|
-
type_str = {'StatusUpdate'=>'update', 'StatusResponse'=>'response'}[type]
|
58
|
-
raise RSMP::TimeoutError.new "Did not received status #{type_str} in reply to #{m_id} within #{options[:timeout]}s"
|
59
|
-
end
|
60
|
-
|
61
|
-
def wait_for_status_updates_or_responses parent_task, type, options={}, &block
|
62
|
-
raise ArgumentError.new("component argument is missing") unless options[:component]
|
63
|
-
raise ArgumentError.new("status_list argument is missing") unless options[:status_list]
|
64
|
-
m_id = RSMP::Message.make_m_id # make message id so we can start waiting for it
|
65
|
-
|
66
|
-
# wait for command responses in an async task
|
67
|
-
task = parent_task.async do |task|
|
68
|
-
capture_status_updates_or_responses task, type, options, m_id
|
69
|
-
end
|
70
|
-
|
71
|
-
# call block, it should send command request using the given m_id
|
72
|
-
yield m_id
|
73
|
-
|
74
|
-
# wait for the response and return it, raise exception if NotAck received, it it timed out
|
75
|
-
task.wait
|
76
|
-
end
|
77
|
-
|
78
|
-
def wait_for_status_updates parent_task, options={}, &block
|
79
|
-
wait_for_status_updates_or_responses parent_task, 'StatusUpdate', options, &block
|
80
|
-
end
|
81
|
-
|
82
|
-
def wait_for_status_responses parent_task, options={}, &block
|
83
|
-
wait_for_status_updates_or_responses parent_task, 'StatusResponse', options, &block
|
84
|
-
end
|
85
|
-
|
86
|
-
def process_command_response message
|
87
|
-
log "Received #{message.type}", message: message, level: :log
|
88
|
-
acknowledge message
|
89
|
-
end
|
90
|
-
|
91
|
-
def command_match? query, item
|
92
|
-
return false if query[:sCI] && query[:sCI] != item['sCI']
|
93
|
-
return false if query[:n] && query[:n] != item['n']
|
94
|
-
if query[:s].is_a? Regexp
|
95
|
-
return false if query[:v] && item['v'] !~ query[:v]
|
96
|
-
else
|
97
|
-
return false if query[:v] && item['v'] != query[:v]
|
98
|
-
end
|
99
|
-
true
|
100
|
-
end
|
101
|
-
|
102
|
-
def capture_command_responses parent_task, type, options, m_id
|
103
|
-
task.annotate "wait for command response"
|
104
|
-
want = options[:command_list].clone
|
105
|
-
result = {}
|
106
|
-
item = @archive.capture(parent_task,options.merge({
|
107
|
-
type: [type,'MessageNotAck'],
|
108
|
-
num: 1
|
109
|
-
})) do |item|
|
110
|
-
message = item[:message]
|
111
|
-
if message.is_a?(MessageNotAck) && message.attribute('oMId') == m_id
|
112
|
-
# and message.attribute('oMId')==m_id
|
113
|
-
# set result to an exception, but don't raise it.
|
114
|
-
# this will be returned by the task and stored as the task result
|
115
|
-
# when the parent task call wait() on the task, the exception
|
116
|
-
# will be raised in the parent task, and caught by rspec.
|
117
|
-
# rspec will then show the error and record the test as failed
|
118
|
-
m_id_short = RSMP::Message.shorten_m_id m_id, 8
|
119
|
-
result = RSMP::MessageRejected.new "Command request #{m_id_short} was rejected: #{message.attribute('rea')}"
|
120
|
-
next true # done, no more messages wanted
|
121
|
-
end
|
122
|
-
|
123
|
-
found = []
|
124
|
-
# look through querues
|
125
|
-
want.each_with_index do |query,i|
|
126
|
-
# look through items in message
|
127
|
-
item[:message].attributes['rvs'].each do |input|
|
128
|
-
ok = command_match? query, input
|
129
|
-
if ok
|
130
|
-
result[query] = input
|
131
|
-
found << i # record which queries where matched succesfully
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
# remove queries that where matched
|
136
|
-
found.sort.reverse.each do |i|
|
137
|
-
want.delete_at i
|
138
|
-
end
|
139
|
-
want.empty? # any queries left to match?
|
140
|
-
end
|
141
|
-
result
|
142
|
-
rescue Async::TimeoutError
|
143
|
-
raise RSMP::TimeoutError.new "Did not receive command response to #{m_id} within #{options[:timeout]}s"
|
144
|
-
end
|
145
|
-
|
146
|
-
def wait_for_command_responses parent_task, options={}, &block
|
147
|
-
raise ArgumentError.new("component argument is missing") unless options[:component]
|
148
|
-
raise ArgumentError.new("command_list argument is missing") unless options[:command_list]
|
149
|
-
m_id = RSMP::Message.make_m_id # make message id so we can start waiting for it
|
150
|
-
|
151
|
-
# wait for command responses in an async task
|
152
|
-
task = parent_task.async do |task|
|
153
|
-
capture_command_responses task, 'CommandResponse', options, m_id
|
154
|
-
end
|
155
|
-
|
156
|
-
# call block, it should send command request using the given m_id
|
157
|
-
yield m_id
|
158
|
-
|
159
|
-
# wait for the response and return it, raise exception if NotAck received, it it timed out
|
160
|
-
task.wait
|
161
|
-
end
|
162
15
|
end
|
163
16
|
end
|