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.
@@ -58,26 +58,31 @@ module RSMP
58
58
  end
59
59
  end
60
60
 
61
- def start_action
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
- connect socket, info
93
+ accept_connection socket, info
89
94
  else
90
- reject socket, info
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
- def connect socket, info
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.run # will run until the site disconnects
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 reject socket, info
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
- wait_for(@site_id_condition,timeout) { find_site site_id }
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
- wait_for(@site_id_condition,timeout) { true unless find_site site_id }
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
- def start
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
- super
28
- connect
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
- log "Could not connect to supervisor at #{@ip}:#{@port}: Errno #{e.errno} #{e}", level: :error
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
- log "Error while connecting to supervisor at #{@ip}:#{@port}: #{e}", level: :error
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 stop
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 connect
55
- return if @socket
71
+ def connect_tcp
56
72
  @endpoint = Async::IO::Endpoint.tcp(@ip, @port)
57
- @socket = @endpoint.connect
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 connection_complete
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 "Waiting #{interval} seconds before trying to reconnect", level: :info
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
- connection_complete
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 = get_state
12
+ @state = compute_state
13
13
  end
14
14
 
15
- def get_state
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
- @state = node.main.startup_state || 'a'
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
@@ -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 reset
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
- @emergency_route = false
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
- s = "#{group.c_id}:#{group.state}"
132
- if group.state =~ /^[1-9]$/
140
+ state = group.state
141
+ s = "#{group.c_id}:#{state}"
142
+ if state =~ /^[1-9]$/
133
143
  s.colorize(:green)
134
- elsif group.state =~ /^[NOP]$/
144
+ elsif state =~ /^[NOP]$/
135
145
  s.colorize(:yellow)
136
- elsif group.state =~ /^[ae]$/
137
- s.colorize(:black)
138
- elsif group.state =~ /^[f]$/
146
+ elsif state =~ /^[ae]$/
147
+ s.colorize(:light_black)
148
+ elsif state =~ /^[f]$/
139
149
  s.colorize(:yellow)
140
- elsif group.state =~ /^[g]$/
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(4)} #{pos.to_s.rjust(4)} #{str}\r"
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 start_action
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
- task.annotate task_name
71
- next_time = Time.now.to_f
72
- loop do
73
- begin
74
- timer(@clock.now)
75
- rescue EOFError => e
76
- log "Connection closed: #{e}", level: :warning
77
- rescue IOError => e
78
- log "IOError", level: :warning
79
- rescue Errno::ECONNRESET
80
- log "Connection reset by peer", level: :warning
81
- rescue Errno::EPIPE => e
82
- log "Broken pipe", level: :warning
83
- rescue StandardError => e
84
- notify_error e, level: :internal
85
- ensure
86
- # adjust sleep duration to avoid drift. so wake up always happens on the
87
- # same fractional second.
88
- # note that Time.now is not monotonic. If the clock is changed,
89
- # either manaully or via NTP, the sleep interval might jump.
90
- # an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
91
- # to get the current time. this ensures a constant interval, but
92
- # if the clock is changed, the wake up would then happen on a different
93
- # fractional second
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
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = "0.8.6"
2
+ VERSION = "0.9.0"
3
3
  end
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.8.6
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-26 00:00:00.000000000 Z
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