rsmp 0.8.4 → 0.9.1
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/.github/workflows/rspec.yaml +21 -0
- data/.ruby-version +1 -1
- data/Gemfile.lock +51 -67
- data/README.md +2 -12
- data/bin/console +1 -1
- data/cucumber.yml +1 -0
- data/documentation/classes_and_modules.md +4 -4
- data/documentation/collecting_message.md +2 -2
- data/documentation/tasks.md +149 -0
- data/lib/rsmp/archive.rb +3 -3
- data/lib/rsmp/cli.rb +32 -4
- data/lib/rsmp/collect/aggregated_status_collector.rb +1 -1
- data/lib/rsmp/collect/collector.rb +13 -6
- data/lib/rsmp/collect/command_response_collector.rb +1 -1
- data/lib/rsmp/collect/state_collector.rb +1 -1
- data/lib/rsmp/collect/status_collector.rb +2 -1
- data/lib/rsmp/components.rb +3 -3
- data/lib/rsmp/convert/export/json_schema.rb +4 -4
- data/lib/rsmp/convert/import/yaml.rb +1 -1
- data/lib/rsmp/deep_merge.rb +1 -0
- data/lib/rsmp/error.rb +0 -3
- data/lib/rsmp/inspect.rb +1 -1
- data/lib/rsmp/logger.rb +5 -5
- data/lib/rsmp/logging.rb +1 -1
- data/lib/rsmp/message.rb +1 -1
- data/lib/rsmp/node.rb +10 -45
- data/lib/rsmp/proxy.rb +184 -134
- data/lib/rsmp/rsmp.rb +1 -1
- data/lib/rsmp/site.rb +23 -60
- data/lib/rsmp/site_proxy.rb +33 -37
- data/lib/rsmp/supervisor.rb +25 -21
- data/lib/rsmp/supervisor_proxy.rb +58 -29
- data/lib/rsmp/task.rb +84 -0
- data/lib/rsmp/tlc/signal_group.rb +5 -3
- data/lib/rsmp/tlc/signal_plan.rb +2 -2
- data/lib/rsmp/tlc/traffic_controller.rb +97 -29
- data/lib/rsmp/tlc/traffic_controller_site.rb +43 -36
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +1 -1
- data/rsmp.gemspec +7 -7
- metadata +21 -20
- data/lib/rsmp/site_proxy_wait.rb +0 -0
- data/lib/rsmp/wait.rb +0 -16
- data/test.rb +0 -27
data/lib/rsmp/site_proxy.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Handles a supervisor connection to a remote client
|
2
2
|
|
3
|
-
module RSMP
|
3
|
+
module RSMP
|
4
4
|
class SiteProxy < Proxy
|
5
5
|
include Components
|
6
6
|
|
@@ -14,33 +14,37 @@ module RSMP
|
|
14
14
|
@site_id = options[:site_id]
|
15
15
|
end
|
16
16
|
|
17
|
+
# handle communication
|
18
|
+
# when we're created, the socket is already open
|
19
|
+
def run
|
20
|
+
set_state :connected
|
21
|
+
start_reader
|
22
|
+
wait_for_reader # run until disconnected
|
23
|
+
rescue RSMP::ConnectionError => e
|
24
|
+
log e, level: :error
|
25
|
+
rescue StandardError => e
|
26
|
+
notify_error e, level: :internal
|
27
|
+
ensure
|
28
|
+
close
|
29
|
+
end
|
30
|
+
|
17
31
|
def revive options
|
18
32
|
super options
|
19
33
|
@supervisor = options[:supervisor]
|
20
34
|
@settings = @supervisor.supervisor_settings.clone
|
21
35
|
end
|
22
36
|
|
23
|
-
|
24
37
|
def inspect
|
25
38
|
"#<#{self.class.name}:#{self.object_id}, #{inspector(
|
26
39
|
:@acknowledgements,:@settings,:@site_settings,:@components
|
27
40
|
)}>"
|
28
41
|
end
|
42
|
+
|
29
43
|
def node
|
30
44
|
supervisor
|
31
45
|
end
|
32
46
|
|
33
|
-
def
|
34
|
-
super
|
35
|
-
start_reader
|
36
|
-
end
|
37
|
-
|
38
|
-
def stop
|
39
|
-
log "Closing connection to site", level: :info
|
40
|
-
super
|
41
|
-
end
|
42
|
-
|
43
|
-
def connection_complete
|
47
|
+
def handshake_complete
|
44
48
|
super
|
45
49
|
sanitized_sxl_version = RSMP::Schemer.sanitize_version(@site_sxl_version)
|
46
50
|
log "Connection to site #{@site_id} established, using core #{@rsmp_version}, #{@sxl} #{sanitized_sxl_version}", level: :info
|
@@ -68,7 +72,7 @@ module RSMP
|
|
68
72
|
else
|
69
73
|
super message
|
70
74
|
end
|
71
|
-
rescue RSMP::RepeatedAlarmError, RSMP::RepeatedStatusError
|
75
|
+
rescue RSMP::RepeatedAlarmError, RSMP::RepeatedStatusError => e
|
72
76
|
str = "Rejected #{message.type} message,"
|
73
77
|
dont_acknowledge message, str, "#{e}"
|
74
78
|
notify_error e.exception("#{str}#{e.message} #{message.json}")
|
@@ -101,13 +105,11 @@ module RSMP
|
|
101
105
|
"cId" => component,
|
102
106
|
"mId" => m_id
|
103
107
|
})
|
104
|
-
send_and_optionally_collect message, options do |
|
105
|
-
|
108
|
+
send_and_optionally_collect message, options do |collect_options|
|
109
|
+
AggregatedStatusCollector.new(
|
106
110
|
self,
|
107
|
-
|
111
|
+
collect_options.merge(task:@task,m_id: m_id, num:1)
|
108
112
|
)
|
109
|
-
collector.collect
|
110
|
-
collector
|
111
113
|
end
|
112
114
|
end
|
113
115
|
|
@@ -150,7 +152,7 @@ module RSMP
|
|
150
152
|
end
|
151
153
|
|
152
154
|
def version_acknowledged
|
153
|
-
|
155
|
+
handshake_complete
|
154
156
|
end
|
155
157
|
|
156
158
|
def process_watchdog message
|
@@ -179,14 +181,12 @@ module RSMP
|
|
179
181
|
"sS" => request_list,
|
180
182
|
"mId" => m_id
|
181
183
|
})
|
182
|
-
send_and_optionally_collect message, options do |
|
183
|
-
|
184
|
+
send_and_optionally_collect message, options do |collect_options|
|
185
|
+
StatusCollector.new(
|
184
186
|
self,
|
185
187
|
status_list,
|
186
|
-
|
188
|
+
collect_options.merge(task:@task,m_id: m_id)
|
187
189
|
)
|
188
|
-
collector.collect
|
189
|
-
collector
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
@@ -200,7 +200,7 @@ module RSMP
|
|
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
|
203
|
-
|
203
|
+
|
204
204
|
# additional items can be used when verifying the response,
|
205
205
|
# but must to remove from the subscribe message
|
206
206
|
subscribe_list = status_list.map { |item| item.slice('sCI','n','uRt') }
|
@@ -215,14 +215,12 @@ module RSMP
|
|
215
215
|
"sS" => subscribe_list,
|
216
216
|
'mId' => m_id
|
217
217
|
})
|
218
|
-
send_and_optionally_collect message, options do |
|
219
|
-
|
218
|
+
send_and_optionally_collect message, options do |collect_options|
|
219
|
+
StatusCollector.new(
|
220
220
|
self,
|
221
221
|
status_list,
|
222
|
-
|
222
|
+
collect_options.merge(task:@task,m_id: m_id)
|
223
223
|
)
|
224
|
-
collector.collect
|
225
|
-
collector
|
226
224
|
end
|
227
225
|
end
|
228
226
|
|
@@ -267,14 +265,12 @@ module RSMP
|
|
267
265
|
"arg" => command_list,
|
268
266
|
"mId" => m_id
|
269
267
|
})
|
270
|
-
send_and_optionally_collect message, options do |
|
271
|
-
|
268
|
+
send_and_optionally_collect message, options do |collect_options|
|
269
|
+
CommandResponseCollector.new(
|
272
270
|
self,
|
273
271
|
command_list,
|
274
|
-
|
272
|
+
collect_options.merge(task:@task,m_id: m_id)
|
275
273
|
)
|
276
|
-
collector.collect
|
277
|
-
collector
|
278
274
|
end
|
279
275
|
end
|
280
276
|
|
@@ -332,7 +328,7 @@ module RSMP
|
|
332
328
|
log "Using site settings for guest", level: :debug
|
333
329
|
return @settings['guest']
|
334
330
|
end
|
335
|
-
|
331
|
+
|
336
332
|
nil
|
337
333
|
end
|
338
334
|
|
data/lib/rsmp/supervisor.rb
CHANGED
@@ -58,26 +58,31 @@ module RSMP
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
|
61
|
+
# listen for connections
|
62
|
+
# Async::IO::Endpoint#accept createa an async task that we will wait for
|
63
|
+
def run
|
64
|
+
log "Starting supervisor on port #{@supervisor_settings["port"]}",
|
65
|
+
level: :info,
|
66
|
+
timestamp: @clock.now
|
67
|
+
|
62
68
|
@endpoint = Async::IO::Endpoint.tcp('0.0.0.0', @supervisor_settings["port"])
|
63
|
-
@endpoint.accept do |socket| # creates async tasks
|
69
|
+
tasks = @endpoint.accept do |socket| # creates async tasks
|
64
70
|
handle_connection(socket)
|
65
71
|
rescue StandardError => e
|
66
72
|
notify_error e, level: :internal
|
67
73
|
end
|
74
|
+
tasks.each { |task| task.wait }
|
68
75
|
rescue StandardError => e
|
69
76
|
notify_error e, level: :internal
|
70
77
|
end
|
71
78
|
|
79
|
+
# stop
|
72
80
|
def stop
|
73
81
|
log "Stopping supervisor #{@supervisor_settings["site_id"]}", level: :info
|
74
|
-
@proxies.each { |proxy| proxy.stop }
|
75
|
-
@proxies.clear
|
76
82
|
super
|
77
|
-
@tcp_server.close if @tcp_server
|
78
|
-
@tcp_server = nil
|
79
83
|
end
|
80
84
|
|
85
|
+
# handle an incoming connction by either accepting of rejecting it
|
81
86
|
def handle_connection socket
|
82
87
|
remote_port = socket.remote_address.ip_port
|
83
88
|
remote_hostname = socket.remote_address.ip_address
|
@@ -85,9 +90,9 @@ module RSMP
|
|
85
90
|
|
86
91
|
info = {ip:remote_ip, port:remote_port, hostname:remote_hostname, now:Clock.now}
|
87
92
|
if accept? socket, info
|
88
|
-
|
93
|
+
accept_connection socket, info
|
89
94
|
else
|
90
|
-
|
95
|
+
reject_connection socket, info
|
91
96
|
end
|
92
97
|
rescue ConnectionError => e
|
93
98
|
log "Rejected connection from #{remote_ip}:#{remote_port}, #{e.to_s}", level: :warning
|
@@ -99,12 +104,6 @@ module RSMP
|
|
99
104
|
close socket, info
|
100
105
|
end
|
101
106
|
|
102
|
-
def starting
|
103
|
-
log "Starting supervisor on port #{@supervisor_settings["port"]}",
|
104
|
-
level: :info,
|
105
|
-
timestamp: @clock.now
|
106
|
-
end
|
107
|
-
|
108
107
|
def accept? socket, info
|
109
108
|
true
|
110
109
|
end
|
@@ -143,7 +142,8 @@ module RSMP
|
|
143
142
|
message.attribute('siteId').first['sId']
|
144
143
|
end
|
145
144
|
|
146
|
-
|
145
|
+
# accept an incoming connecting by creating and starting a proxy
|
146
|
+
def accept_connection socket, info
|
147
147
|
log "Site connected from #{format_ip_and_port(info)}",
|
148
148
|
ip: info[:ip],
|
149
149
|
port: info[:port],
|
@@ -182,7 +182,8 @@ module RSMP
|
|
182
182
|
proxy = build_proxy settings.merge(site_id:id) # keep the id learned by peeking above
|
183
183
|
@proxies.push proxy
|
184
184
|
end
|
185
|
-
proxy.
|
185
|
+
proxy.start # will run until the site disconnects
|
186
|
+
proxy.wait
|
186
187
|
ensure
|
187
188
|
site_ids_changed
|
188
189
|
stop if @supervisor_settings['one_shot']
|
@@ -192,7 +193,7 @@ module RSMP
|
|
192
193
|
@site_id_condition.signal
|
193
194
|
end
|
194
195
|
|
195
|
-
def
|
196
|
+
def reject_connection socket, info
|
196
197
|
log "Site rejected", ip: info[:ip], level: :info
|
197
198
|
end
|
198
199
|
|
@@ -224,10 +225,13 @@ module RSMP
|
|
224
225
|
nil
|
225
226
|
end
|
226
227
|
|
227
|
-
def wait_for_site site_id, timeout
|
228
|
+
def wait_for_site site_id, timeout:
|
228
229
|
site = find_site site_id
|
229
230
|
return site if site
|
230
|
-
|
231
|
+
wait_for_condition(@site_id_condition,timeout:timeout) do
|
232
|
+
find_site site_id
|
233
|
+
end
|
234
|
+
|
231
235
|
rescue Async::TimeoutError
|
232
236
|
if site_id == :any
|
233
237
|
str = "No site connected"
|
@@ -237,8 +241,8 @@ module RSMP
|
|
237
241
|
raise RSMP::TimeoutError.new "#{str} within #{timeout}s"
|
238
242
|
end
|
239
243
|
|
240
|
-
def wait_for_site_disconnect site_id, timeout
|
241
|
-
|
244
|
+
def wait_for_site_disconnect site_id, timeout:
|
245
|
+
wait_for_condition(@site_id_condition,timeout:timeout) { true unless find_site site_id }
|
242
246
|
rescue Async::TimeoutError
|
243
247
|
raise RSMP::TimeoutError.new "Site '#{site_id}' did not disconnect within #{timeout}s"
|
244
248
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'digest'
|
4
4
|
|
5
|
-
module RSMP
|
5
|
+
module RSMP
|
6
6
|
class SupervisorProxy < Proxy
|
7
7
|
|
8
8
|
attr_reader :supervisor_id, :site
|
@@ -22,44 +22,70 @@ module RSMP
|
|
22
22
|
site
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
# handle communication
|
26
|
+
# if disconnected, then try to reconnect
|
27
|
+
def run
|
28
|
+
loop do
|
29
|
+
connect
|
30
|
+
start_reader
|
31
|
+
start_handshake
|
32
|
+
wait_for_reader # run until disconnected
|
33
|
+
break if reconnect_delay == false
|
34
|
+
rescue Restart
|
35
|
+
@logger.mute @ip, @port
|
36
|
+
raise
|
37
|
+
rescue RSMP::ConnectionError => e
|
38
|
+
log e, level: :error
|
39
|
+
break if reconnect_delay == false
|
40
|
+
rescue StandardError => e
|
41
|
+
notify_error e, level: :internal
|
42
|
+
break if reconnect_delay == false
|
43
|
+
ensure
|
44
|
+
close
|
45
|
+
stop_subtasks
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def start_handshake
|
50
|
+
send_version @site_settings['site_id'], @site_settings["rsmp_versions"]
|
51
|
+
end
|
52
|
+
|
53
|
+
# connect to the supervisor and initiate handshake supervisor
|
54
|
+
def connect
|
26
55
|
log "Connecting to supervisor at #{@ip}:#{@port}", level: :info
|
27
|
-
|
28
|
-
|
56
|
+
set_state :connecting
|
57
|
+
connect_tcp
|
29
58
|
@logger.unmute @ip, @port
|
30
59
|
log "Connected to supervisor at #{@ip}:#{@port}", level: :info
|
31
|
-
start_reader
|
32
|
-
send_version @site_settings['site_id'], @site_settings["rsmp_versions"]
|
33
60
|
rescue SystemCallError => e
|
34
|
-
|
35
|
-
retry_notice
|
61
|
+
raise ConnectionError.new "Could not connect to supervisor at #{@ip}:#{@port}: Errno #{e.errno} #{e}"
|
36
62
|
rescue StandardError => e
|
37
|
-
|
38
|
-
retry_notice
|
39
|
-
end
|
40
|
-
|
41
|
-
def retry_notice
|
42
|
-
unless @site.site_settings['intervals']['reconnect'] == :no
|
43
|
-
log "Will try to reconnect again every #{@site.site_settings['intervals']['reconnect']} seconds..", level: :info
|
44
|
-
@logger.mute @ip, @port
|
45
|
-
end
|
63
|
+
raise ConnectionError.new "Error while connecting to supervisor at #{@ip}:#{@port}: #{e}"
|
46
64
|
end
|
47
65
|
|
48
|
-
def
|
49
|
-
log "Closing connection to supervisor", level: :info
|
66
|
+
def stop_task
|
50
67
|
super
|
51
68
|
@last_status_sent = nil
|
52
69
|
end
|
53
70
|
|
54
|
-
def
|
55
|
-
return if @socket
|
71
|
+
def connect_tcp
|
56
72
|
@endpoint = Async::IO::Endpoint.tcp(@ip, @port)
|
57
|
-
|
73
|
+
|
74
|
+
# Async::IO::Endpoint#connect renames the current task. run in a subtask to avoid this see issue #22
|
75
|
+
@task.async do |task|
|
76
|
+
task.annotate 'socket task'
|
77
|
+
# this timeout is a workaround for #connect hanging on windows if the other side is not present yet
|
78
|
+
task.with_timeout 1.1 do
|
79
|
+
@socket = @endpoint.connect
|
80
|
+
end
|
81
|
+
end.wait
|
82
|
+
|
58
83
|
@stream = Async::IO::Stream.new(@socket)
|
59
84
|
@protocol = Async::IO::Protocol::Line.new(@stream,WRAPPING_DELIMITER) # rsmp messages are json terminated with a form-feed
|
85
|
+
set_state :connected
|
60
86
|
end
|
61
87
|
|
62
|
-
def
|
88
|
+
def handshake_complete
|
63
89
|
super
|
64
90
|
sanitized_sxl_version = RSMP::Schemer.sanitize_version(sxl_version)
|
65
91
|
log "Connection to supervisor established, using core #{@rsmp_version}, #{sxl} #{sanitized_sxl_version}", level: :info
|
@@ -114,16 +140,19 @@ module RSMP
|
|
114
140
|
end
|
115
141
|
|
116
142
|
def reconnect_delay
|
143
|
+
return false if @site_settings['intervals']['reconnect'] == :no
|
117
144
|
interval = @site_settings['intervals']['reconnect']
|
118
|
-
log "
|
145
|
+
log "Will try to reconnect again every #{interval} seconds...", level: :info
|
146
|
+
@logger.mute @ip, @port
|
119
147
|
@task.sleep interval
|
148
|
+
true
|
120
149
|
end
|
121
150
|
|
122
151
|
def version_accepted message
|
123
152
|
log "Received Version message, using RSMP #{@rsmp_version}", message: message, level: :log
|
124
153
|
start_timer
|
125
154
|
acknowledge message
|
126
|
-
|
155
|
+
handshake_complete
|
127
156
|
@version_determined = true
|
128
157
|
end
|
129
158
|
|
@@ -138,8 +167,8 @@ module RSMP
|
|
138
167
|
"mId" => m_id,
|
139
168
|
})
|
140
169
|
|
141
|
-
send_and_optionally_collect message, options do |
|
142
|
-
|
170
|
+
send_and_optionally_collect message, options do |collect_options|
|
171
|
+
Collector.new self, collect_options.merge(task:@task, type: 'MessageAck')
|
143
172
|
end
|
144
173
|
end
|
145
174
|
|
@@ -234,7 +263,7 @@ module RSMP
|
|
234
263
|
update_list = {}
|
235
264
|
component = message.attributes["cId"]
|
236
265
|
@status_subscriptions[component] ||= {}
|
237
|
-
update_list[component] ||= {}
|
266
|
+
update_list[component] ||= {}
|
238
267
|
now = Time.now # internal timestamp
|
239
268
|
subs = @status_subscriptions[component]
|
240
269
|
|
@@ -299,7 +328,7 @@ module RSMP
|
|
299
328
|
by_name.each_pair do |name,subscription|
|
300
329
|
current = nil
|
301
330
|
should_send = false
|
302
|
-
if subscription[:interval] == 0
|
331
|
+
if subscription[:interval] == 0
|
303
332
|
# send as soon as the data changes
|
304
333
|
if component_object
|
305
334
|
current, age = *(component_object.get_status code, name)
|
data/lib/rsmp/task.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
module RSMP
|
2
|
+
class Restart < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
module Task
|
6
|
+
attr_reader :task
|
7
|
+
|
8
|
+
def initialize_task
|
9
|
+
@task = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# start our async tasks and return immediately
|
13
|
+
# run() will be called inside the task to perform actual long-running work
|
14
|
+
def start
|
15
|
+
return if @task
|
16
|
+
Async do |task|
|
17
|
+
task.annotate "#{self.class.name} main task"
|
18
|
+
@task = task
|
19
|
+
run
|
20
|
+
stop_subtasks
|
21
|
+
@task = nil
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
# initiate restart by raising a Restart exception
|
27
|
+
def restart
|
28
|
+
raise Restart.new "restart initiated by #{self.class.name}:#{object_id}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# get the status of our task, or nil of no task
|
32
|
+
def task_status
|
33
|
+
@task.status if @task
|
34
|
+
end
|
35
|
+
|
36
|
+
# perform any long-running work
|
37
|
+
# the method will be called from an async task, and should not return
|
38
|
+
# if subtasks are needed, the method should call wait() on each of them
|
39
|
+
# once running, ready() must be called
|
40
|
+
def run
|
41
|
+
start_subtasks
|
42
|
+
end
|
43
|
+
|
44
|
+
# wait for our task to complete
|
45
|
+
def wait
|
46
|
+
@task.wait if @task
|
47
|
+
end
|
48
|
+
|
49
|
+
# stop our task
|
50
|
+
def stop
|
51
|
+
stop_subtasks
|
52
|
+
stop_task if @task
|
53
|
+
end
|
54
|
+
|
55
|
+
def stop_subtasks
|
56
|
+
end
|
57
|
+
|
58
|
+
# stop our task and any subtask
|
59
|
+
def stop_task
|
60
|
+
@task.stop
|
61
|
+
@task = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# wait for an async condition to signal, then yield to block
|
65
|
+
# if block returns true we're done. otherwise, wait again
|
66
|
+
def wait_for_condition condition, timeout:, task:Async::Task.current, &block
|
67
|
+
unless task
|
68
|
+
raise RuntimeError.new("Can't wait without a task")
|
69
|
+
end
|
70
|
+
task.with_timeout(timeout) do
|
71
|
+
while task.running?
|
72
|
+
value = condition.wait
|
73
|
+
return value unless block
|
74
|
+
result = yield value
|
75
|
+
return result if result
|
76
|
+
end
|
77
|
+
raise RuntimeError.new("Can't wait for condition because task #{task.object_id} #{task.annotation} is not running")
|
78
|
+
end
|
79
|
+
rescue Async::TimeoutError
|
80
|
+
raise RSMP::TimeoutError.new
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -9,25 +9,27 @@ module RSMP
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def timer
|
12
|
-
@state =
|
12
|
+
@state = compute_state
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def compute_state
|
16
16
|
return 'a' if node.main.dark_mode
|
17
17
|
return 'c' if node.main.yellow_flash
|
18
18
|
|
19
19
|
cycle_counter = node.main.cycle_counter
|
20
20
|
|
21
21
|
if node.main.startup_sequence_active
|
22
|
-
|
22
|
+
return node.main.startup_state || 'a'
|
23
23
|
end
|
24
24
|
|
25
25
|
default = 'a' # phase a means disabled/dark
|
26
26
|
plan = node.main.current_plan
|
27
27
|
return default unless plan
|
28
28
|
return default unless plan.states
|
29
|
+
|
29
30
|
states = plan.states[c_id]
|
30
31
|
return default unless states
|
32
|
+
|
31
33
|
state = states[cycle_counter]
|
32
34
|
return default unless state =~ /[a-hA-G0-9N-P]/ # valid signal group states
|
33
35
|
state
|
data/lib/rsmp/tlc/signal_plan.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module RSMP
|
2
2
|
module TLC
|
3
3
|
# A Traffic Light Controller Signal Plan.
|
4
|
-
# A signal plan is a description of how all signal groups should change
|
5
|
-
# state over time.
|
4
|
+
# A signal plan is a description of how all signal groups should change
|
5
|
+
# state over time.
|
6
6
|
class SignalPlan
|
7
7
|
attr_reader :nr, :states, :dynamic_bands
|
8
8
|
def initialize nr:, states:, dynamic_bands:
|