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