rsmp 0.1.19 → 0.1.31
Sign up to get free protection for your applications and to get access to all the features.
- 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
|