rsmp 0.8.0 → 0.8.4
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/Gemfile.lock +3 -3
- data/config/tlc.yaml +8 -6
- data/documentation/classes_and_modules.md +1 -4
- data/documentation/collecting_message.md +196 -0
- data/lib/rsmp/collect/aggregated_status_collector.rb +9 -0
- data/lib/rsmp/collect/collector.rb +92 -59
- data/lib/rsmp/collect/command_query.rb +16 -0
- data/lib/rsmp/collect/command_response_collector.rb +20 -0
- data/lib/rsmp/collect/filter.rb +31 -0
- data/lib/rsmp/collect/listener.rb +0 -8
- data/lib/rsmp/collect/message_matchers.rb +0 -64
- data/lib/rsmp/collect/query.rb +16 -6
- data/lib/rsmp/collect/{matcher.rb → state_collector.rb} +19 -9
- data/lib/rsmp/collect/status_collector.rb +20 -0
- data/lib/rsmp/collect/{message_queries.rb → status_query.rb} +0 -15
- data/lib/rsmp/node.rb +0 -1
- data/lib/rsmp/proxy.rb +15 -19
- data/lib/rsmp/site.rb +3 -2
- data/lib/rsmp/site_proxy.rb +34 -46
- data/lib/rsmp/supervisor_proxy.rb +3 -11
- data/lib/rsmp/tlc/signal_group.rb +15 -7
- data/lib/rsmp/tlc/traffic_controller.rb +92 -28
- data/lib/rsmp/tlc/traffic_controller_site.rb +10 -1
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +7 -3
- metadata +10 -5
- data/lib/rsmp/collect/message_collector.rb +0 -209
data/lib/rsmp/collect/query.rb
CHANGED
@@ -8,26 +8,36 @@ module RSMP
|
|
8
8
|
@want = want
|
9
9
|
@got = nil
|
10
10
|
@message = nil
|
11
|
-
@done = false
|
12
11
|
end
|
13
12
|
|
14
13
|
# Are we done, i.e. did the last checked item match?
|
15
14
|
def done?
|
16
|
-
@
|
15
|
+
@got != nil
|
17
16
|
end
|
18
17
|
|
19
18
|
# Check an item and set @done to true if it matches
|
20
19
|
# Store the item and corresponding message if there's a positive or negative match
|
21
|
-
def perform_match item, message
|
20
|
+
def perform_match item, message, block
|
22
21
|
matched = match? item
|
23
22
|
if matched != nil
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
if block
|
24
|
+
status = block.call(nil,item)
|
25
|
+
matched = status if status == true || status == false
|
26
|
+
end
|
27
27
|
end
|
28
28
|
matched
|
29
29
|
end
|
30
30
|
|
31
|
+
def keep message, item
|
32
|
+
@message = message
|
33
|
+
@got = item
|
34
|
+
end
|
35
|
+
|
36
|
+
def forget
|
37
|
+
@message = nil
|
38
|
+
@got = nil
|
39
|
+
end
|
40
|
+
|
31
41
|
def match? item
|
32
42
|
end
|
33
43
|
end
|
@@ -31,8 +31,7 @@ module RSMP
|
|
31
31
|
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/} =>
|
32
32
|
# { <StatusResponse message>, {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>"9"} }
|
33
33
|
# }
|
34
|
-
|
35
|
-
class Matcher < Collector
|
34
|
+
class StateCollector < Collector
|
36
35
|
attr_reader :queries
|
37
36
|
|
38
37
|
# Initialize with a list of wanted statuses
|
@@ -62,7 +61,7 @@ module RSMP
|
|
62
61
|
|
63
62
|
# Get messages from results
|
64
63
|
def messages
|
65
|
-
@queries.map { |query| query.message }.uniq
|
64
|
+
@queries.map { |query| query.message }.uniq.compact
|
66
65
|
end
|
67
66
|
|
68
67
|
# Return progress as completes queries vs. total number of queries
|
@@ -91,22 +90,33 @@ module RSMP
|
|
91
90
|
# Check if a messages matches our criteria.
|
92
91
|
# Match each query against each item in the message
|
93
92
|
def perform_match message
|
94
|
-
return
|
93
|
+
return false if super(message) == false
|
94
|
+
return unless collecting?
|
95
95
|
@queries.each do |query| # look through queries
|
96
96
|
get_items(message).each do |item| # look through items in message
|
97
|
-
matched = query.perform_match(item,message)
|
98
|
-
|
99
|
-
matched = @block.call(message,item) if @block
|
100
|
-
end
|
97
|
+
matched = query.perform_match(item,message,@block)
|
98
|
+
return unless collecting?
|
101
99
|
if matched != nil
|
102
100
|
type = {true=>'match',false=>'mismatch'}[matched]
|
103
101
|
@notifier.log "#{@title.capitalize} #{message.m_id_short} collect #{type} #{query.want}, item #{item}", level: :debug
|
104
|
-
|
102
|
+
if matched == true
|
103
|
+
query.keep message, item
|
104
|
+
elsif matched == false
|
105
|
+
query.forget
|
106
|
+
end
|
105
107
|
end
|
106
108
|
end
|
107
109
|
end
|
108
110
|
complete if done?
|
109
111
|
@notifier.log "#{@title.capitalize} collect reached #{summary}", level: :debug
|
110
112
|
end
|
113
|
+
|
114
|
+
# don't collect anything. Query will collect them instead
|
115
|
+
def keep message
|
116
|
+
end
|
117
|
+
|
118
|
+
def describe
|
119
|
+
@queries.map {|q| q.want.to_s }
|
120
|
+
end
|
111
121
|
end
|
112
122
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RSMP
|
2
|
+
# Base class for waiting for status updates or responses
|
3
|
+
class StatusCollector < StateCollector
|
4
|
+
def initialize proxy, want, options={}
|
5
|
+
super proxy, want, options.merge(title: 'status response', type:['MessageNotAck'])
|
6
|
+
|
7
|
+
@options[:type] << 'StatusUpdate' unless options[:updates] == false
|
8
|
+
@options[:type] << 'StatusResponse' unless options[:reponses] == false
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_query want
|
12
|
+
RSMP::StatusQuery.new want
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get items, in our case status values
|
16
|
+
def get_items message
|
17
|
+
message.attributes['sS'] || []
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,19 +1,4 @@
|
|
1
1
|
module RSMP
|
2
|
-
# Match a specific command responses
|
3
|
-
class CommandQuery < Query
|
4
|
-
# Match a return value item against a query
|
5
|
-
def match? item
|
6
|
-
return nil if @want['cCI'] && @want['cCI'] != item['cCI']
|
7
|
-
return nil if @want['n'] && @want['n'] != item['n']
|
8
|
-
if @want['v'].is_a? Regexp
|
9
|
-
return false if @want['v'] && item['v'] !~ @want['v']
|
10
|
-
else
|
11
|
-
return false if @want['v'] && item['v'] != @want['v']
|
12
|
-
end
|
13
|
-
true
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
2
|
# Match a specific status response or update
|
18
3
|
class StatusQuery < Query
|
19
4
|
# Match a status value against a query
|
data/lib/rsmp/node.rb
CHANGED
data/lib/rsmp/proxy.rb
CHANGED
@@ -52,12 +52,6 @@ module RSMP
|
|
52
52
|
node.clock
|
53
53
|
end
|
54
54
|
|
55
|
-
def collect task, options, &block
|
56
|
-
collector = RSMP::Collector.new self, options
|
57
|
-
collector.collect task, &block
|
58
|
-
collector
|
59
|
-
end
|
60
|
-
|
61
55
|
def run
|
62
56
|
start
|
63
57
|
@reader.wait if @reader
|
@@ -579,25 +573,27 @@ module RSMP
|
|
579
573
|
end
|
580
574
|
|
581
575
|
def wait_for_acknowledgement parent_task, options={}, m_id
|
582
|
-
|
583
|
-
|
584
|
-
num: 1
|
585
|
-
})) do |message|
|
576
|
+
collector = Collector.new self, options.merge(task: parent_task, type: ['MessageAck','MessageNotAck'])
|
577
|
+
collector.collect do |message|
|
586
578
|
if message.is_a?(MessageNotAck)
|
587
579
|
if message.attribute('oMId') == m_id
|
588
|
-
# set result to an exception, but don't raise it.
|
589
|
-
# this will be returned by the task and stored as the task result
|
590
|
-
# when the parent task call wait() on the task, the exception
|
591
|
-
# will be raised in the parent task, and caught by rspec.
|
592
|
-
# rspec will then show the error and record the test as failed
|
593
580
|
m_id_short = RSMP::Message.shorten_m_id m_id, 8
|
594
|
-
|
595
|
-
next true # done, no more messages wanted
|
581
|
+
raise RSMP::MessageRejected.new "Aggregated status request #{m_id_short} was rejected with '#{message.attribute('rea')}'"
|
596
582
|
end
|
597
583
|
elsif message.is_a?(MessageAck)
|
598
|
-
|
584
|
+
collector.complete if message.attribute('oMId') == m_id
|
599
585
|
end
|
600
|
-
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
def send_and_optionally_collect message, options, &block
|
590
|
+
if options[:collect]
|
591
|
+
task = @task.async { |task| yield task }
|
592
|
+
send_message message, validate: options[:validate]
|
593
|
+
{ sent: message, collector: task.wait }
|
594
|
+
else
|
595
|
+
send_message message, validate: options[:validate]
|
596
|
+
return { sent: message }
|
601
597
|
end
|
602
598
|
end
|
603
599
|
end
|
data/lib/rsmp/site.rb
CHANGED
@@ -9,9 +9,9 @@ module RSMP
|
|
9
9
|
attr_reader :rsmp_versions, :site_settings, :logger, :proxies
|
10
10
|
|
11
11
|
def initialize options={}
|
12
|
+
super options
|
12
13
|
initialize_components
|
13
14
|
handle_site_settings options
|
14
|
-
super options
|
15
15
|
@proxies = []
|
16
16
|
@sleep_condition = Async::Notification.new
|
17
17
|
@proxies_condition = Async::Notification.new
|
@@ -95,7 +95,8 @@ module RSMP
|
|
95
95
|
ip: supervisor_settings['ip'],
|
96
96
|
port: supervisor_settings['port'],
|
97
97
|
logger: @logger,
|
98
|
-
archive: @archive
|
98
|
+
archive: @archive,
|
99
|
+
collect: @collect
|
99
100
|
})
|
100
101
|
@proxies << proxy
|
101
102
|
@proxies_condition.signal
|
data/lib/rsmp/site_proxy.rb
CHANGED
@@ -101,8 +101,13 @@ module RSMP
|
|
101
101
|
"cId" => component,
|
102
102
|
"mId" => m_id
|
103
103
|
})
|
104
|
-
|
105
|
-
|
104
|
+
send_and_optionally_collect message, options do |task|
|
105
|
+
collector = AggregatedStatusCollector.new(
|
106
|
+
self,
|
107
|
+
options[:collect].merge(task:@task,m_id: m_id, num:1)
|
108
|
+
)
|
109
|
+
collector.collect
|
110
|
+
collector
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
@@ -174,8 +179,14 @@ module RSMP
|
|
174
179
|
"sS" => request_list,
|
175
180
|
"mId" => m_id
|
176
181
|
})
|
177
|
-
|
178
|
-
|
182
|
+
send_and_optionally_collect message, options do |task|
|
183
|
+
collector = StatusCollector.new(
|
184
|
+
self,
|
185
|
+
status_list,
|
186
|
+
options[:collect].merge(task:@task,m_id: m_id)
|
187
|
+
)
|
188
|
+
collector.collect
|
189
|
+
collector
|
179
190
|
end
|
180
191
|
end
|
181
192
|
|
@@ -186,17 +197,6 @@ module RSMP
|
|
186
197
|
acknowledge message
|
187
198
|
end
|
188
199
|
|
189
|
-
def send_and_collect_if_needed message, options, &block
|
190
|
-
if options[:collect]
|
191
|
-
task = @task.async { |task| yield task }
|
192
|
-
send_message message, validate: options[:validate]
|
193
|
-
{ sent: message, collector: task.wait }
|
194
|
-
else
|
195
|
-
send_message message, validate: options[:validate]
|
196
|
-
return { sent: message }
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
200
|
def subscribe_to_status component_id, status_list, options={}
|
201
201
|
validate_ready 'subscribe to status'
|
202
202
|
m_id = options[:m_id] || RSMP::Message.make_m_id
|
@@ -215,8 +215,14 @@ module RSMP
|
|
215
215
|
"sS" => subscribe_list,
|
216
216
|
'mId' => m_id
|
217
217
|
})
|
218
|
-
|
219
|
-
|
218
|
+
send_and_optionally_collect message, options do |task|
|
219
|
+
collector = StatusCollector.new(
|
220
|
+
self,
|
221
|
+
status_list,
|
222
|
+
options[:collect].merge(task:@task,m_id: m_id)
|
223
|
+
)
|
224
|
+
collector.collect
|
225
|
+
collector
|
220
226
|
end
|
221
227
|
end
|
222
228
|
|
@@ -261,8 +267,14 @@ module RSMP
|
|
261
267
|
"arg" => command_list,
|
262
268
|
"mId" => m_id
|
263
269
|
})
|
264
|
-
|
265
|
-
|
270
|
+
send_and_optionally_collect message, options do |task|
|
271
|
+
collector = CommandResponseCollector.new(
|
272
|
+
self,
|
273
|
+
command_list,
|
274
|
+
options[:collect].merge(task:@task,m_id: m_id)
|
275
|
+
)
|
276
|
+
collector.collect
|
277
|
+
collector
|
266
278
|
end
|
267
279
|
end
|
268
280
|
|
@@ -339,37 +351,13 @@ module RSMP
|
|
339
351
|
distribute_error e, options
|
340
352
|
end
|
341
353
|
|
342
|
-
def collect_alarms
|
343
|
-
collect(
|
354
|
+
def collect_alarms options={}
|
355
|
+
collect(@task,options.merge(type: "Alarm")) do |alarm|
|
344
356
|
next if options[:aCId] && options[:aCId] != alarm.attribute("aCId")
|
345
357
|
next if options[:aSp] && options[:aSp] != alarm.attribute("aSp")
|
346
358
|
next if options[:aS] && options[:aS] != alarm.attribute("aS")
|
347
|
-
|
359
|
+
:keep
|
348
360
|
end
|
349
361
|
end
|
350
|
-
|
351
|
-
def collect_status_updates task, status_list, options
|
352
|
-
collector = StatusUpdateMatcher.new(self, status_list, options)
|
353
|
-
collector.collect task
|
354
|
-
collector
|
355
|
-
end
|
356
|
-
|
357
|
-
def collect_status_responses task, status_list, options
|
358
|
-
collector = StatusResponseMatcher.new(self, status_list, options)
|
359
|
-
collector.collect task
|
360
|
-
collector
|
361
|
-
end
|
362
|
-
|
363
|
-
def collect_command_responses task, command_list, options
|
364
|
-
collector = CommandResponseMatcher.new(self, command_list, options)
|
365
|
-
collector.collect task
|
366
|
-
collector
|
367
|
-
end
|
368
|
-
|
369
|
-
def collect_aggregated_status task, options
|
370
|
-
collector = AggregatedStatusMatcher.new(self, options)
|
371
|
-
collector.collect task
|
372
|
-
collector
|
373
|
-
end
|
374
362
|
end
|
375
363
|
end
|
@@ -138,16 +138,8 @@ module RSMP
|
|
138
138
|
"mId" => m_id,
|
139
139
|
})
|
140
140
|
|
141
|
-
|
142
|
-
|
143
|
-
task = @task.async do |task|
|
144
|
-
wait_for_acknowledgement task, options[:collect], m_id
|
145
|
-
end
|
146
|
-
send_message message, validate: options[:validate]
|
147
|
-
return message, task.wait
|
148
|
-
else
|
149
|
-
send_message message, validate: options[:validate]
|
150
|
-
message
|
141
|
+
send_and_optionally_collect message, options do |task|
|
142
|
+
wait_for_acknowledgement task, options[:collect], m_id
|
151
143
|
end
|
152
144
|
end
|
153
145
|
|
@@ -282,7 +274,7 @@ module RSMP
|
|
282
274
|
end
|
283
275
|
|
284
276
|
def fetch_last_sent_status component, code, name
|
285
|
-
@last_status_sent.dig component, code, name
|
277
|
+
@last_status_sent.dig component, code, name if @last_status_sent
|
286
278
|
end
|
287
279
|
|
288
280
|
def store_last_sent_status message
|
@@ -6,25 +6,33 @@ module RSMP
|
|
6
6
|
# plan is a string, with each character representing a signal phase at a particular second in the cycle
|
7
7
|
def initialize node:, id:
|
8
8
|
super node: node, id: id, grouped: false
|
9
|
-
move 0
|
10
9
|
end
|
11
10
|
|
12
|
-
def
|
11
|
+
def timer
|
12
|
+
@state = get_state
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_state
|
16
|
+
return 'a' if node.main.dark_mode
|
17
|
+
return 'c' if node.main.yellow_flash
|
18
|
+
|
19
|
+
cycle_counter = node.main.cycle_counter
|
20
|
+
|
21
|
+
if node.main.startup_sequence_active
|
22
|
+
@state = node.main.startup_state || 'a'
|
23
|
+
end
|
24
|
+
|
13
25
|
default = 'a' # phase a means disabled/dark
|
14
26
|
plan = node.main.current_plan
|
15
27
|
return default unless plan
|
16
28
|
return default unless plan.states
|
17
29
|
states = plan.states[c_id]
|
18
30
|
return default unless states
|
19
|
-
state =states[
|
31
|
+
state = states[cycle_counter]
|
20
32
|
return default unless state =~ /[a-hA-G0-9N-P]/ # valid signal group states
|
21
33
|
state
|
22
34
|
end
|
23
35
|
|
24
|
-
def move pos
|
25
|
-
@state = get_state pos
|
26
|
-
end
|
27
|
-
|
28
36
|
def handle_command command_code, arg
|
29
37
|
case command_code
|
30
38
|
when 'M0010', 'M0011'
|
@@ -5,9 +5,12 @@ module RSMP
|
|
5
5
|
# and keeps track of signal plans, detector logics, inputs, etc. which do
|
6
6
|
# not have dedicated components.
|
7
7
|
class TrafficController < Component
|
8
|
-
attr_reader :pos, :cycle_time, :plan
|
8
|
+
attr_reader :pos, :cycle_time, :plan, :cycle_counter,
|
9
|
+
:yellow_flash, :dark_mode,
|
10
|
+
:startup_sequence_active, :startup_sequence, :startup_sequence_pos
|
9
11
|
|
10
|
-
def initialize node:, id:, cycle_time: 10, signal_plans
|
12
|
+
def initialize node:, id:, cycle_time: 10, signal_plans:,
|
13
|
+
startup_sequence:, live_output:nil
|
11
14
|
super node: node, id: id, grouped: true
|
12
15
|
@signal_groups = []
|
13
16
|
@detector_logics = []
|
@@ -15,13 +18,15 @@ module RSMP
|
|
15
18
|
@cycle_time = cycle_time
|
16
19
|
@num_traffic_situations = 1
|
17
20
|
@num_inputs = 8
|
21
|
+
@startup_sequence = startup_sequence
|
22
|
+
@live_output = live_output
|
18
23
|
reset
|
19
24
|
end
|
20
25
|
|
21
26
|
def reset
|
22
|
-
@
|
23
|
-
@plan =
|
24
|
-
@dark_mode =
|
27
|
+
@cycle_counter = 0
|
28
|
+
@plan = 1
|
29
|
+
@dark_mode = true
|
25
30
|
@yellow_flash = false
|
26
31
|
@booting = false
|
27
32
|
@control_mode = 'control'
|
@@ -40,6 +45,12 @@ module RSMP
|
|
40
45
|
@inputs = '0'*@num_inputs
|
41
46
|
@input_activations = '0'*@num_inputs
|
42
47
|
@input_results = '0'*@num_inputs
|
48
|
+
|
49
|
+
@day_time_table = {}
|
50
|
+
@startup_sequence_active = false
|
51
|
+
@startup_sequence_initiated_at = nil
|
52
|
+
@startup_sequence_pos = 0
|
53
|
+
@time_int = nil
|
43
54
|
end
|
44
55
|
|
45
56
|
def clock
|
@@ -64,46 +75,83 @@ module RSMP
|
|
64
75
|
end
|
65
76
|
|
66
77
|
def timer now
|
67
|
-
# TODO
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
78
|
+
# TODO use monotone timer, to avoid jumps in case the user sets the system time
|
79
|
+
@signal_groups.each { |group| group.timer }
|
80
|
+
time = Time.now.to_i
|
81
|
+
return if time == @time_int
|
82
|
+
@time_int = time
|
83
|
+
move_cycle_counter
|
84
|
+
move_startup_sequence if @startup_sequence_active
|
85
|
+
output_states
|
86
|
+
end
|
87
|
+
|
88
|
+
def move_cycle_counter
|
89
|
+
counter = Time.now.to_i % @cycle_time
|
90
|
+
@cycle_counter = counter
|
91
|
+
end
|
92
|
+
|
93
|
+
def startup_state
|
94
|
+
return unless @startup_sequence_active
|
95
|
+
return unless @startup_sequence_pos
|
96
|
+
@startup_sequence[ @startup_sequence_pos ]
|
75
97
|
end
|
76
98
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
99
|
+
def initiate_startup_sequence
|
100
|
+
log "Initiating startup sequence", level: :info
|
101
|
+
@startup_sequence_active = true
|
102
|
+
@startup_sequence_initiated_at = nil
|
103
|
+
@startup_sequence_pos = nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def end_startup_sequence
|
107
|
+
@startup_sequence_active = false
|
108
|
+
@startup_sequence_initiated_at = nil
|
109
|
+
@startup_sequence_pos = nil
|
110
|
+
|
111
|
+
@yellow_flash = false
|
112
|
+
@dark_mode = false
|
113
|
+
end
|
114
|
+
|
115
|
+
def move_startup_sequence
|
116
|
+
was = @startup_sequence_pos
|
117
|
+
if @startup_sequence_initiated_at == nil
|
118
|
+
@startup_sequence_initiated_at = Time.now.to_i + 1
|
119
|
+
@startup_sequence_pos = 0
|
120
|
+
else
|
121
|
+
@startup_sequence_pos = Time.now.to_i - @startup_sequence_initiated_at
|
122
|
+
end
|
123
|
+
if @startup_sequence_pos >= @startup_sequence.size
|
124
|
+
end_startup_sequence
|
80
125
|
end
|
81
|
-
#output_states
|
82
126
|
end
|
83
127
|
|
84
128
|
def output_states
|
129
|
+
return unless @live_output
|
85
130
|
str = @signal_groups.map do |group|
|
86
131
|
s = "#{group.c_id}:#{group.state}"
|
87
132
|
if group.state =~ /^[1-9]$/
|
88
133
|
s.colorize(:green)
|
89
134
|
elsif group.state =~ /^[NOP]$/
|
90
135
|
s.colorize(:yellow)
|
91
|
-
elsif group.state =~ /^[
|
92
|
-
s.colorize(
|
136
|
+
elsif group.state =~ /^[ae]$/
|
137
|
+
s.colorize(:black)
|
138
|
+
elsif group.state =~ /^[f]$/
|
139
|
+
s.colorize(:yellow)
|
140
|
+
elsif group.state =~ /^[g]$/
|
141
|
+
s.colorize(:red)
|
93
142
|
else
|
94
143
|
s.colorize(:red)
|
95
144
|
end
|
96
145
|
end.join ' '
|
97
146
|
plan = "P#{@plan}"
|
98
|
-
|
147
|
+
|
148
|
+
File.open @live_output, 'w' do |file|
|
149
|
+
file.puts "#{plan.rjust(4)} #{pos.to_s.rjust(4)} #{str}\r"
|
150
|
+
end
|
99
151
|
end
|
100
152
|
|
101
153
|
def format_signal_group_status
|
102
|
-
|
103
|
-
'c' * @signal_groups.size
|
104
|
-
else
|
105
|
-
@signal_groups.map { |group| group.state }.join
|
106
|
-
end
|
154
|
+
@signal_groups.map { |group| group.state }.join
|
107
155
|
end
|
108
156
|
|
109
157
|
def handle_command command_code, arg
|
@@ -214,6 +262,18 @@ module RSMP
|
|
214
262
|
|
215
263
|
def handle_m0017 arg
|
216
264
|
@node.verify_security_code 2, arg['securityCode']
|
265
|
+
arg['status'].split(',').each do |item|
|
266
|
+
elems = item.split('-')
|
267
|
+
nr = elems[0].to_i
|
268
|
+
plan = elems[1].to_i
|
269
|
+
hour = elems[2].to_i
|
270
|
+
min = elems[3].to_i
|
271
|
+
if nr<0 || nr>12
|
272
|
+
raise InvalidMessage.new "time table id must be between 0 and 12, got #{nr}"
|
273
|
+
end
|
274
|
+
#p "nr: #{nr}, plan #{plan} at #{hour}:#{min}"
|
275
|
+
@day_time_table[nr] = {plan: plan, hour: hour, min:min}
|
276
|
+
end
|
217
277
|
end
|
218
278
|
|
219
279
|
def handle_m0018 arg
|
@@ -276,6 +336,7 @@ module RSMP
|
|
276
336
|
log "Switching to mode #{mode}", level: :info
|
277
337
|
case mode
|
278
338
|
when 'NormalControl'
|
339
|
+
initiate_startup_sequence if @yellow_flash || @dark_mode
|
279
340
|
@yellow_flash = false
|
280
341
|
@dark_mode = false
|
281
342
|
when 'YellowFlash'
|
@@ -308,9 +369,9 @@ module RSMP
|
|
308
369
|
when 'signalgroupstatus'
|
309
370
|
TrafficControllerSite.make_status format_signal_group_status
|
310
371
|
when 'cyclecounter'
|
311
|
-
TrafficControllerSite.make_status @
|
372
|
+
TrafficControllerSite.make_status @cycle_counter.to_s
|
312
373
|
when 'basecyclecounter'
|
313
|
-
TrafficControllerSite.make_status @
|
374
|
+
TrafficControllerSite.make_status @cycle_counter.to_s
|
314
375
|
when 'stage'
|
315
376
|
TrafficControllerSite.make_status 0.to_s
|
316
377
|
end
|
@@ -511,7 +572,10 @@ module RSMP
|
|
511
572
|
def handle_s0027 status_code, status_name=nil
|
512
573
|
case status_name
|
513
574
|
when 'status'
|
514
|
-
|
575
|
+
status = @day_time_table.map do |i,item|
|
576
|
+
"#{i}-#{item[:plan]}-#{item[:hour]}-#{item[:min]}"
|
577
|
+
end.join(',')
|
578
|
+
TrafficControllerSite.make_status status
|
515
579
|
end
|
516
580
|
end
|
517
581
|
|
@@ -5,11 +5,16 @@ module RSMP
|
|
5
5
|
attr_accessor :main, :signal_plans
|
6
6
|
|
7
7
|
def initialize options={}
|
8
|
+
# setup options before calling super initializer,
|
9
|
+
# since build of components depend on options
|
8
10
|
@sxl = 'traffic_light_controller'
|
9
11
|
@security_codes = options[:site_settings]['security_codes']
|
10
12
|
@interval = options[:site_settings].dig('intervals','timer') || 1
|
13
|
+
@startup_sequence = options[:site_settings]['startup_sequence'] || 'efg'
|
11
14
|
build_plans options[:site_settings].dig('signal_plans')
|
15
|
+
|
12
16
|
super options
|
17
|
+
|
13
18
|
unless @main
|
14
19
|
raise ConfigurationError.new "TLC must have a main component"
|
15
20
|
end
|
@@ -37,7 +42,9 @@ module RSMP
|
|
37
42
|
when 'main'
|
38
43
|
@main = TrafficController.new node: self, id: id,
|
39
44
|
cycle_time: settings['cycle_time'],
|
40
|
-
|
45
|
+
startup_sequence: @startup_sequence,
|
46
|
+
signal_plans: @signal_plans,
|
47
|
+
live_output: @site_settings['live_output']
|
41
48
|
when 'signal_group'
|
42
49
|
group = SignalGroup.new node: self, id: id
|
43
50
|
@main.add_signal_group group
|
@@ -52,6 +59,7 @@ module RSMP
|
|
52
59
|
def start_action
|
53
60
|
super
|
54
61
|
start_timer
|
62
|
+
@main.initiate_startup_sequence
|
55
63
|
end
|
56
64
|
|
57
65
|
def start_timer
|
@@ -134,6 +142,7 @@ module RSMP
|
|
134
142
|
when :restart
|
135
143
|
log "Restarting TLC", level: :info
|
136
144
|
restart
|
145
|
+
initiate_startup_sequence
|
137
146
|
end
|
138
147
|
end
|
139
148
|
end
|
data/lib/rsmp/version.rb
CHANGED