rsmp 0.8.6 → 0.9.0
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 +14 -0
- data/Gemfile.lock +2 -1
- data/README.md +2 -2
- data/bin/console +1 -1
- 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 +27 -4
- data/lib/rsmp/collect/state_collector.rb +1 -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/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 +176 -119
- data/lib/rsmp/rsmp.rb +1 -1
- data/lib/rsmp/site.rb +23 -60
- data/lib/rsmp/site_proxy.rb +21 -17
- data/lib/rsmp/supervisor.rb +25 -21
- data/lib/rsmp/supervisor_proxy.rb +53 -27
- 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 +42 -20
- data/lib/rsmp/tlc/traffic_controller_site.rb +43 -36
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +1 -1
- metadata +5 -5
- data/lib/rsmp/site_proxy_wait.rb +0 -0
- data/lib/rsmp/wait.rb +0 -16
- data/test.rb +0 -27
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,67 @@ 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
|
75
|
+
@task.async do |task|
|
76
|
+
task.annotate 'socket task'
|
77
|
+
@socket = @endpoint.connect
|
78
|
+
end.wait
|
79
|
+
|
58
80
|
@stream = Async::IO::Stream.new(@socket)
|
59
81
|
@protocol = Async::IO::Protocol::Line.new(@stream,WRAPPING_DELIMITER) # rsmp messages are json terminated with a form-feed
|
82
|
+
set_state :connected
|
60
83
|
end
|
61
84
|
|
62
|
-
def
|
85
|
+
def handshake_complete
|
63
86
|
super
|
64
87
|
sanitized_sxl_version = RSMP::Schemer.sanitize_version(sxl_version)
|
65
88
|
log "Connection to supervisor established, using core #{@rsmp_version}, #{sxl} #{sanitized_sxl_version}", level: :info
|
@@ -114,16 +137,19 @@ module RSMP
|
|
114
137
|
end
|
115
138
|
|
116
139
|
def reconnect_delay
|
140
|
+
return false if @site_settings['intervals']['reconnect'] == :no
|
117
141
|
interval = @site_settings['intervals']['reconnect']
|
118
|
-
log "
|
142
|
+
log "Will try to reconnect again every #{interval} seconds...", level: :info
|
143
|
+
@logger.mute @ip, @port
|
119
144
|
@task.sleep interval
|
145
|
+
true
|
120
146
|
end
|
121
147
|
|
122
148
|
def version_accepted message
|
123
149
|
log "Received Version message, using RSMP #{@rsmp_version}", message: message, level: :log
|
124
150
|
start_timer
|
125
151
|
acknowledge message
|
126
|
-
|
152
|
+
handshake_complete
|
127
153
|
@version_determined = true
|
128
154
|
end
|
129
155
|
|
@@ -234,7 +260,7 @@ module RSMP
|
|
234
260
|
update_list = {}
|
235
261
|
component = message.attributes["cId"]
|
236
262
|
@status_subscriptions[component] ||= {}
|
237
|
-
update_list[component] ||= {}
|
263
|
+
update_list[component] ||= {}
|
238
264
|
now = Time.now # internal timestamp
|
239
265
|
subs = @status_subscriptions[component]
|
240
266
|
|
@@ -299,7 +325,7 @@ module RSMP
|
|
299
325
|
by_name.each_pair do |name,subscription|
|
300
326
|
current = nil
|
301
327
|
should_send = false
|
302
|
-
if subscription[:interval] == 0
|
328
|
+
if subscription[:interval] == 0
|
303
329
|
# send as soon as the data changes
|
304
330
|
if component_object
|
305
331
|
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 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:
|
@@ -23,24 +23,29 @@ module RSMP
|
|
23
23
|
reset
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
@cycle_counter = 0
|
28
|
-
@plan = 1
|
26
|
+
def reset_modes
|
29
27
|
@dark_mode = true
|
30
28
|
@yellow_flash = false
|
31
29
|
@booting = false
|
32
|
-
@control_mode = 'control'
|
33
|
-
@police_key = 0
|
34
|
-
@intersection = 0
|
35
30
|
@is_starting = false
|
36
|
-
@
|
37
|
-
@emergency_route_number = 0
|
38
|
-
@traffic_situation = 0
|
31
|
+
@control_mode = 'control'
|
39
32
|
@manual_control = false
|
40
33
|
@fixed_time_control = false
|
41
34
|
@isolated_control = false
|
42
35
|
@yellow_flash = false
|
43
36
|
@all_red = false
|
37
|
+
@police_key = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def reset
|
41
|
+
reset_modes
|
42
|
+
|
43
|
+
@cycle_counter = 0
|
44
|
+
@plan = 1
|
45
|
+
@intersection = 0
|
46
|
+
@emergency_route = false
|
47
|
+
@emergency_route_number = 0
|
48
|
+
@traffic_situation = 0
|
44
49
|
|
45
50
|
@inputs = '0'*@num_inputs
|
46
51
|
@input_activations = '0'*@num_inputs
|
@@ -76,12 +81,14 @@ module RSMP
|
|
76
81
|
|
77
82
|
def timer now
|
78
83
|
# TODO use monotone timer, to avoid jumps in case the user sets the system time
|
79
|
-
@signal_groups.each { |group| group.timer }
|
80
84
|
time = Time.now.to_i
|
81
85
|
return if time == @time_int
|
82
86
|
@time_int = time
|
83
87
|
move_cycle_counter
|
84
88
|
move_startup_sequence if @startup_sequence_active
|
89
|
+
|
90
|
+
@signal_groups.each { |group| group.timer }
|
91
|
+
|
85
92
|
output_states
|
86
93
|
end
|
87
94
|
|
@@ -98,6 +105,8 @@ module RSMP
|
|
98
105
|
|
99
106
|
def initiate_startup_sequence
|
100
107
|
log "Initiating startup sequence", level: :info
|
108
|
+
reset_modes
|
109
|
+
@dark_mode = false
|
101
110
|
@startup_sequence_active = true
|
102
111
|
@startup_sequence_initiated_at = nil
|
103
112
|
@startup_sequence_pos = nil
|
@@ -107,7 +116,6 @@ module RSMP
|
|
107
116
|
@startup_sequence_active = false
|
108
117
|
@startup_sequence_initiated_at = nil
|
109
118
|
@startup_sequence_pos = nil
|
110
|
-
|
111
119
|
@yellow_flash = false
|
112
120
|
@dark_mode = false
|
113
121
|
end
|
@@ -127,26 +135,40 @@ module RSMP
|
|
127
135
|
|
128
136
|
def output_states
|
129
137
|
return unless @live_output
|
138
|
+
|
130
139
|
str = @signal_groups.map do |group|
|
131
|
-
|
132
|
-
|
140
|
+
state = group.state
|
141
|
+
s = "#{group.c_id}:#{state}"
|
142
|
+
if state =~ /^[1-9]$/
|
133
143
|
s.colorize(:green)
|
134
|
-
elsif
|
144
|
+
elsif state =~ /^[NOP]$/
|
135
145
|
s.colorize(:yellow)
|
136
|
-
elsif
|
137
|
-
s.colorize(:
|
138
|
-
elsif
|
146
|
+
elsif state =~ /^[ae]$/
|
147
|
+
s.colorize(:light_black)
|
148
|
+
elsif state =~ /^[f]$/
|
139
149
|
s.colorize(:yellow)
|
140
|
-
elsif
|
150
|
+
elsif state =~ /^[g]$/
|
141
151
|
s.colorize(:red)
|
142
152
|
else
|
143
153
|
s.colorize(:red)
|
144
154
|
end
|
145
155
|
end.join ' '
|
156
|
+
|
157
|
+
modes = '.'*9
|
158
|
+
modes[0] = 'B' if @booting
|
159
|
+
modes[1] = 'S' if @startup_sequence_active
|
160
|
+
modes[2] = 'D' if @dark_mode
|
161
|
+
modes[3] = 'Y' if @yellow_flash
|
162
|
+
modes[4] = 'M' if @manual_control
|
163
|
+
modes[5] = 'F' if @fixed_time_control
|
164
|
+
modes[6] = 'R' if @all_red
|
165
|
+
modes[7] = 'I' if @isolated_control
|
166
|
+
modes[8] = 'P' if @police_key != 0
|
167
|
+
|
146
168
|
plan = "P#{@plan}"
|
147
169
|
|
148
170
|
File.open @live_output, 'w' do |file|
|
149
|
-
file.puts "#{plan.rjust(
|
171
|
+
file.puts "#{modes} #{plan.rjust(2)} #{@cycle_counter.to_s.rjust(3)} #{str}\r"
|
150
172
|
end
|
151
173
|
end
|
152
174
|
|
@@ -316,7 +338,7 @@ module RSMP
|
|
316
338
|
return unless i>=0 && i<@num_inputs
|
317
339
|
@inputs[i] = (arg['value'] ? '1' : '0')
|
318
340
|
end
|
319
|
-
|
341
|
+
|
320
342
|
def set_fixed_time_control status
|
321
343
|
@fixed_time_control = status
|
322
344
|
end
|
@@ -18,6 +18,18 @@ module RSMP
|
|
18
18
|
unless @main
|
19
19
|
raise ConfigurationError.new "TLC must have a main component"
|
20
20
|
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def start
|
25
|
+
super
|
26
|
+
start_tlc_timer
|
27
|
+
@main.initiate_startup_sequence
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop_subtasks
|
31
|
+
stop_tlc_timer
|
32
|
+
super
|
21
33
|
end
|
22
34
|
|
23
35
|
def build_plans signal_plans
|
@@ -56,49 +68,45 @@ module RSMP
|
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
59
|
-
def
|
60
|
-
super
|
61
|
-
start_timer
|
62
|
-
@main.initiate_startup_sequence
|
63
|
-
end
|
64
|
-
|
65
|
-
def start_timer
|
71
|
+
def start_tlc_timer
|
66
72
|
task_name = "tlc timer"
|
67
73
|
log "Starting #{task_name} with interval #{@interval} seconds", level: :debug
|
68
74
|
|
69
75
|
@timer = @task.async do |task|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
next_time += @interval
|
95
|
-
duration = next_time - Time.now.to_f
|
96
|
-
task.sleep duration
|
97
|
-
end
|
76
|
+
task.annotate task_name
|
77
|
+
run_tlc_timer task
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def run_tlc_timer task
|
82
|
+
next_time = Time.now.to_f
|
83
|
+
loop do
|
84
|
+
begin
|
85
|
+
timer(@clock.now)
|
86
|
+
rescue StandardError => e
|
87
|
+
notify_error e, level: :internal
|
88
|
+
ensure
|
89
|
+
# adjust sleep duration to avoid drift. so wake up always happens on the
|
90
|
+
# same fractional second.
|
91
|
+
# note that Time.now is not monotonic. If the clock is changed,
|
92
|
+
# either manaully or via NTP, the sleep interval might jump.
|
93
|
+
# an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
|
94
|
+
# to get the current time. this ensures a constant interval, but
|
95
|
+
# if the clock is changed, the wake up would then happen on a different
|
96
|
+
# fractional second
|
97
|
+
next_time += @interval
|
98
|
+
duration = next_time - Time.now.to_f
|
99
|
+
task.sleep duration
|
98
100
|
end
|
99
101
|
end
|
100
102
|
end
|
101
103
|
|
104
|
+
def stop_tlc_timer
|
105
|
+
return unless @timer
|
106
|
+
@timer.stop
|
107
|
+
@timer = nil
|
108
|
+
end
|
109
|
+
|
102
110
|
def timer now
|
103
111
|
return unless @main
|
104
112
|
@main.timer now
|
@@ -142,7 +150,6 @@ module RSMP
|
|
142
150
|
when :restart
|
143
151
|
log "Restarting TLC", level: :info
|
144
152
|
restart
|
145
|
-
initiate_startup_sequence
|
146
153
|
end
|
147
154
|
end
|
148
155
|
end
|
data/lib/rsmp/version.rb
CHANGED
data/lib/rsmp.rb
CHANGED
@@ -10,10 +10,10 @@ require 'json_schemer'
|
|
10
10
|
require 'async/queue'
|
11
11
|
|
12
12
|
require 'rsmp/rsmp'
|
13
|
+
require 'rsmp/task'
|
13
14
|
require 'rsmp/deep_merge'
|
14
15
|
require 'rsmp/inspect'
|
15
16
|
require 'rsmp/logging'
|
16
|
-
require 'rsmp/wait'
|
17
17
|
require 'rsmp/node'
|
18
18
|
require 'rsmp/supervisor'
|
19
19
|
require 'rsmp/components'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rsmp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emil Tin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01-
|
11
|
+
date: 2022-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -186,6 +186,7 @@ executables:
|
|
186
186
|
extensions: []
|
187
187
|
extra_rdoc_files: []
|
188
188
|
files:
|
189
|
+
- ".github/workflows/rspec.yaml"
|
189
190
|
- ".gitignore"
|
190
191
|
- ".gitmodules"
|
191
192
|
- ".rspec"
|
@@ -203,6 +204,7 @@ files:
|
|
203
204
|
- documentation/classes_and_modules.md
|
204
205
|
- documentation/collecting_message.md
|
205
206
|
- documentation/message_distribution.md
|
207
|
+
- documentation/tasks.md
|
206
208
|
- exe/rsmp
|
207
209
|
- lib/rsmp.rb
|
208
210
|
- lib/rsmp/archive.rb
|
@@ -234,18 +236,16 @@ files:
|
|
234
236
|
- lib/rsmp/rsmp.rb
|
235
237
|
- lib/rsmp/site.rb
|
236
238
|
- lib/rsmp/site_proxy.rb
|
237
|
-
- lib/rsmp/site_proxy_wait.rb
|
238
239
|
- lib/rsmp/supervisor.rb
|
239
240
|
- lib/rsmp/supervisor_proxy.rb
|
241
|
+
- lib/rsmp/task.rb
|
240
242
|
- lib/rsmp/tlc/detector_logic.rb
|
241
243
|
- lib/rsmp/tlc/signal_group.rb
|
242
244
|
- lib/rsmp/tlc/signal_plan.rb
|
243
245
|
- lib/rsmp/tlc/traffic_controller.rb
|
244
246
|
- lib/rsmp/tlc/traffic_controller_site.rb
|
245
247
|
- lib/rsmp/version.rb
|
246
|
-
- lib/rsmp/wait.rb
|
247
248
|
- rsmp.gemspec
|
248
|
-
- test.rb
|
249
249
|
homepage: https://github.com/rsmp-nordic/rsmp
|
250
250
|
licenses:
|
251
251
|
- MIT
|