rsmp 0.1.11 → 0.1.21
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/.gitignore +1 -0
- data/Gemfile.lock +50 -52
- data/README.md +7 -1
- data/config/site.yaml +2 -2
- data/config/tlc.yaml +44 -0
- data/documentation/classes_and_modules.md +65 -0
- data/documentation/message_distribution.md +23 -0
- data/lib/rsmp.rb +10 -5
- data/lib/rsmp/archive.rb +16 -18
- data/lib/rsmp/cli.rb +15 -5
- data/lib/rsmp/collector.rb +90 -0
- data/lib/rsmp/component.rb +12 -2
- data/lib/rsmp/{site_base.rb → components.rb} +8 -6
- data/lib/rsmp/error.rb +10 -1
- data/lib/rsmp/listener.rb +33 -0
- data/lib/rsmp/logger.rb +3 -3
- data/lib/rsmp/{base.rb → logging.rb} +2 -2
- data/lib/rsmp/message.rb +25 -2
- data/lib/rsmp/node.rb +37 -12
- data/lib/rsmp/notifier.rb +24 -0
- data/lib/rsmp/proxy.rb +65 -45
- data/lib/rsmp/site.rb +12 -5
- data/lib/rsmp/site_proxy.rb +91 -65
- data/lib/rsmp/site_proxy_wait.rb +181 -0
- data/lib/rsmp/supervisor.rb +7 -6
- data/lib/rsmp/supervisor_proxy.rb +84 -32
- data/lib/rsmp/tlc.rb +885 -0
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp/wait.rb +7 -8
- data/rsmp.gemspec +10 -11
- metadata +40 -43
- data/lib/rsmp/probe.rb +0 -114
- data/lib/rsmp/probe_collection.rb +0 -28
- data/lib/rsmp/supervisor_base.rb +0 -10
data/lib/rsmp/supervisor.rb
CHANGED
@@ -98,7 +98,7 @@ module RSMP
|
|
98
98
|
true
|
99
99
|
end
|
100
100
|
|
101
|
-
def
|
101
|
+
def build_proxy settings
|
102
102
|
SiteProxy.new settings
|
103
103
|
end
|
104
104
|
|
@@ -117,18 +117,19 @@ module RSMP
|
|
117
117
|
level: :info,
|
118
118
|
timestamp: RSMP.now_object
|
119
119
|
|
120
|
-
|
120
|
+
settings = @supervisor_settings['sites'][info[:ip]] || @supervisor_settings['sites'][:any]
|
121
|
+
proxy = build_proxy({
|
121
122
|
supervisor: self,
|
122
123
|
task: @task,
|
123
|
-
settings:
|
124
|
+
settings: settings,
|
124
125
|
socket: socket,
|
125
126
|
info: info,
|
126
127
|
logger: @logger,
|
127
128
|
archive: @archive
|
128
129
|
})
|
129
130
|
@proxies.push proxy
|
130
|
-
|
131
131
|
proxy.run # will run until the site disconnects
|
132
|
+
ensure
|
132
133
|
@proxies.delete proxy
|
133
134
|
site_ids_changed
|
134
135
|
|
@@ -167,13 +168,13 @@ module RSMP
|
|
167
168
|
def wait_for_site site_id, timeout
|
168
169
|
site = find_site site_id
|
169
170
|
return site if site
|
170
|
-
|
171
|
+
wait_for(@site_id_condition,timeout) { find_site site_id }
|
171
172
|
rescue Async::TimeoutError
|
172
173
|
nil
|
173
174
|
end
|
174
175
|
|
175
176
|
def wait_for_site_disconnect site_id, timeout
|
176
|
-
|
177
|
+
wait_for(@site_id_condition,timeout) { true unless find_site site_id }
|
177
178
|
rescue Async::TimeoutError
|
178
179
|
false
|
179
180
|
end
|
@@ -14,7 +14,6 @@ module RSMP
|
|
14
14
|
@ip = options[:ip]
|
15
15
|
@port = options[:port]
|
16
16
|
@status_subscriptions = {}
|
17
|
-
@status_subscriptions_mutex = Mutex.new
|
18
17
|
@sxl = @site_settings['sxl']
|
19
18
|
@synthetic_id = Supervisor.build_id_from_ip_port @ip, @port
|
20
19
|
end
|
@@ -28,6 +27,7 @@ module RSMP
|
|
28
27
|
super
|
29
28
|
connect
|
30
29
|
@logger.unmute @ip, @port
|
30
|
+
log "Connected to superviser at #{@ip}:#{@port}", level: :info
|
31
31
|
start_reader
|
32
32
|
send_version @site_settings['site_id'], @site_settings["rsmp_versions"]
|
33
33
|
rescue Errno::ECONNREFUSED
|
@@ -38,6 +38,12 @@ module RSMP
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
def stop
|
42
|
+
log "Closing connection to supervisor", level: :info
|
43
|
+
super
|
44
|
+
@last_status_sent = nil
|
45
|
+
end
|
46
|
+
|
41
47
|
def connect
|
42
48
|
return if @socket
|
43
49
|
@endpoint = Async::IO::Endpoint.tcp(@ip, @port)
|
@@ -72,6 +78,13 @@ module RSMP
|
|
72
78
|
else
|
73
79
|
super message
|
74
80
|
end
|
81
|
+
rescue UnknownComponent, UnknownCommand, UnknownStatus,
|
82
|
+
MessageRejected, MissingAttribute => e
|
83
|
+
dont_acknowledge message, '', e.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def process_deferred
|
87
|
+
site.process_deferred
|
75
88
|
end
|
76
89
|
|
77
90
|
def acknowledged_first_ingoing message
|
@@ -112,7 +125,7 @@ module RSMP
|
|
112
125
|
message = AggregatedStatus.new({
|
113
126
|
"aSTS" => RSMP.now_string,
|
114
127
|
"cId" => component.c_id,
|
115
|
-
"fP" =>
|
128
|
+
"fP" => 'NormalControl',
|
116
129
|
"fS" => nil,
|
117
130
|
"se" => component.aggregated_status_bools
|
118
131
|
})
|
@@ -135,22 +148,35 @@ module RSMP
|
|
135
148
|
acknowledge message
|
136
149
|
end
|
137
150
|
|
151
|
+
# reorganize rmsp command request arg attribute:
|
152
|
+
# [{"cCI":"M0002","cO":"setPlan","n":"status","v":"True"},{"cCI":"M0002","cO":"setPlan","n":"securityCode","v":"5678"},{"cCI":"M0002","cO":"setPlan","n":"timeplan","v":"3"}]
|
153
|
+
# into the simpler, but equivalent:
|
154
|
+
# {"M0002"=>{"status"=>"True", "securityCode"=>"5678", "timeplan"=>"3"}}
|
155
|
+
def simplify_command_requests arg
|
156
|
+
sorted = {}
|
157
|
+
arg.each do |item|
|
158
|
+
sorted[item['cCI']] ||= {}
|
159
|
+
sorted[item['cCI']][item['n']] = item['v']
|
160
|
+
end
|
161
|
+
sorted
|
162
|
+
end
|
163
|
+
|
138
164
|
def process_command_request message
|
139
165
|
log "Received #{message.type}", message: message, level: :log
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
146
|
-
rvs << { "cCI" => arg["cCI"],
|
147
|
-
"n" => arg["n"],
|
148
|
-
"v" => arg["v"],
|
149
|
-
"age" => "recent" }
|
166
|
+
component_id = message.attributes["cId"]
|
167
|
+
component = @site.find_component component_id
|
168
|
+
commands = simplify_command_requests message.attributes["arg"]
|
169
|
+
commands.each_pair do |command_code,arg|
|
170
|
+
component.handle_command command_code,arg
|
150
171
|
end
|
151
172
|
|
173
|
+
rvs = message.attributes["arg"].map do |item|
|
174
|
+
item = item.dup.merge('age'=>'recent')
|
175
|
+
item.delete 'cO'
|
176
|
+
item
|
177
|
+
end
|
152
178
|
response = CommandResponse.new({
|
153
|
-
"cId"=>
|
179
|
+
"cId"=>component_id,
|
154
180
|
"cTS"=>RSMP.now_string,
|
155
181
|
"rvs"=>rvs
|
156
182
|
})
|
@@ -158,25 +184,22 @@ module RSMP
|
|
158
184
|
send_message response
|
159
185
|
end
|
160
186
|
|
161
|
-
def process_status_request message
|
187
|
+
def process_status_request message, options={}
|
162
188
|
component_id = message.attributes["cId"]
|
163
189
|
component = @site.find_component component_id
|
164
|
-
|
165
190
|
log "Received #{message.type}", message: message, level: :log
|
166
|
-
sS = message.attributes["sS"].
|
167
|
-
|
168
|
-
|
169
|
-
request
|
191
|
+
sS = message.attributes["sS"].map do |arg|
|
192
|
+
value, quality = component.get_status arg['sCI'], arg['n']
|
193
|
+
{ "s" => value.to_s, "q" => quality.to_s }.merge arg
|
170
194
|
end
|
171
195
|
response = StatusResponse.new({
|
172
196
|
"cId"=>component_id,
|
173
197
|
"sTs"=>RSMP.now_string,
|
174
|
-
"sS"=>sS
|
198
|
+
"sS"=>sS,
|
199
|
+
"mId" => options[:m_id]
|
175
200
|
})
|
176
201
|
acknowledge message
|
177
202
|
send_message response
|
178
|
-
rescue UnknownComponent => e
|
179
|
-
dont_acknowledge message, '', e.to_s
|
180
203
|
end
|
181
204
|
|
182
205
|
def process_status_subcribe message
|
@@ -198,10 +221,11 @@ module RSMP
|
|
198
221
|
update_list[component] ||= {}
|
199
222
|
|
200
223
|
subs = @status_subscriptions[component]
|
224
|
+
now = RSMP::now_object
|
201
225
|
|
202
226
|
message.attributes["sS"].each do |arg|
|
203
227
|
sCI = arg["sCI"]
|
204
|
-
subcription = {interval: arg["uRt"].to_i, last_sent_at:
|
228
|
+
subcription = {interval: arg["uRt"].to_i, last_sent_at: now}
|
205
229
|
subs[sCI] ||= {}
|
206
230
|
subs[sCI][arg["n"]] = subcription
|
207
231
|
|
@@ -235,18 +259,40 @@ module RSMP
|
|
235
259
|
status_update_timer now if ready?
|
236
260
|
end
|
237
261
|
|
262
|
+
def fetch_last_sent_status component, code, name
|
263
|
+
if @last_status_sent && @last_status_sent[component] && @last_status_sent[component][code]
|
264
|
+
@last_status_sent[component][code][name]
|
265
|
+
else
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def store_last_sent_status component, code, name, value
|
271
|
+
@last_status_sent ||= {}
|
272
|
+
@last_status_sent[component] ||= {}
|
273
|
+
@last_status_sent[component][code] ||= {}
|
274
|
+
@last_status_sent[component][code][name] = value
|
275
|
+
end
|
276
|
+
|
238
277
|
def status_update_timer now
|
239
278
|
update_list = {}
|
240
279
|
# go through subscriptons and build a similarly organized list,
|
241
280
|
# that only contains what should be send
|
242
281
|
|
243
282
|
@status_subscriptions.each_pair do |component,by_code|
|
283
|
+
component_object = @site.find_component component
|
244
284
|
by_code.each_pair do |code,by_name|
|
245
285
|
by_name.each_pair do |name,subscription|
|
286
|
+
current = nil
|
246
287
|
if subscription[:interval] == 0
|
247
288
|
# send as soon as the data changes
|
248
|
-
if
|
289
|
+
if component_object
|
290
|
+
current, age = *(component_object.get_status code, name)
|
291
|
+
end
|
292
|
+
last_sent = fetch_last_sent_status component, code, name
|
293
|
+
if current != last_sent
|
249
294
|
should_send = true
|
295
|
+
store_last_sent_status component, code, name, current
|
250
296
|
end
|
251
297
|
else
|
252
298
|
# send at regular intervals
|
@@ -257,8 +303,8 @@ module RSMP
|
|
257
303
|
if should_send
|
258
304
|
subscription[:last_sent_at] = now
|
259
305
|
update_list[component] ||= {}
|
260
|
-
update_list[component][code] ||=
|
261
|
-
update_list[component][code]
|
306
|
+
update_list[component][code] ||= {}
|
307
|
+
update_list[component][code][name] = current
|
262
308
|
end
|
263
309
|
end
|
264
310
|
end
|
@@ -270,18 +316,24 @@ module RSMP
|
|
270
316
|
|
271
317
|
def send_status_updates update_list
|
272
318
|
now = RSMP.now_string
|
273
|
-
update_list.each_pair do |
|
319
|
+
update_list.each_pair do |component_id,by_code|
|
320
|
+
component = @site.find_component component_id
|
274
321
|
sS = []
|
275
322
|
by_code.each_pair do |code,names|
|
276
|
-
names.
|
323
|
+
names.map do |status_name,value|
|
324
|
+
if value
|
325
|
+
quality = 'recent'
|
326
|
+
else
|
327
|
+
value,quality = component.get_status code, status_name
|
328
|
+
end
|
277
329
|
sS << { "sCI" => code,
|
278
|
-
"n" =>
|
279
|
-
"s" =>
|
280
|
-
"q" =>
|
330
|
+
"n" => status_name,
|
331
|
+
"s" => value.to_s,
|
332
|
+
"q" => quality }
|
281
333
|
end
|
282
334
|
end
|
283
335
|
update = StatusUpdate.new({
|
284
|
-
"cId"=>
|
336
|
+
"cId"=>component_id,
|
285
337
|
"sTs"=>now,
|
286
338
|
"sS"=>sS
|
287
339
|
})
|
data/lib/rsmp/tlc.rb
ADDED
@@ -0,0 +1,885 @@
|
|
1
|
+
# Simulates a Traffic Light Controller
|
2
|
+
|
3
|
+
module RSMP
|
4
|
+
|
5
|
+
class TrafficController < Component
|
6
|
+
attr_reader :pos, :cycle_time
|
7
|
+
|
8
|
+
def initialize node:, id:, cycle_time:
|
9
|
+
super node: node, id: id, grouped: true
|
10
|
+
@signal_groups = []
|
11
|
+
@detector_logics = []
|
12
|
+
@plans = []
|
13
|
+
@cycle_time = cycle_time
|
14
|
+
@num_traffic_situations = 1
|
15
|
+
@num_inputs = 8
|
16
|
+
|
17
|
+
reset
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset
|
21
|
+
@pos = 0
|
22
|
+
@plan = 0
|
23
|
+
@dark_mode = false
|
24
|
+
@yellow_flash = false
|
25
|
+
@booting = false
|
26
|
+
@control_mode = 'control'
|
27
|
+
@police_key = 0
|
28
|
+
@intersection = 0
|
29
|
+
@is_starting = false
|
30
|
+
@emergency_route = false
|
31
|
+
@emergency_route_number = 0
|
32
|
+
@traffic_situation = 0
|
33
|
+
@manual_control = false
|
34
|
+
@fixed_time_control = false
|
35
|
+
@isolated_control = false
|
36
|
+
@yellow_flash = false
|
37
|
+
@all_red = false
|
38
|
+
|
39
|
+
@inputs = '0'*@num_inputs
|
40
|
+
@input_activations = '0'*@num_inputs
|
41
|
+
@input_results = '0'*@num_inputs
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_signal_group group
|
45
|
+
@signal_groups << group
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_detector_logic logic
|
49
|
+
@detector_logics << logic
|
50
|
+
end
|
51
|
+
def timer now
|
52
|
+
pos = now.to_i % @cycle_time
|
53
|
+
if pos != @pos
|
54
|
+
@pos = pos
|
55
|
+
move pos
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def move pos
|
60
|
+
@signal_groups.each do |group|
|
61
|
+
group.move pos
|
62
|
+
end
|
63
|
+
if pos == 0
|
64
|
+
aggrated_status_changed
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def output_states
|
69
|
+
str = @signal_groups.map do |group|
|
70
|
+
s = "#{group.c_id}:#{group.state}"
|
71
|
+
if group.state =~ /^[1-9]$/
|
72
|
+
s.colorize(:green)
|
73
|
+
elsif group.state =~ /^[NOP]$/
|
74
|
+
s.colorize(:yellow)
|
75
|
+
else
|
76
|
+
s.colorize(:red)
|
77
|
+
end
|
78
|
+
end.join ' '
|
79
|
+
print "\t#{pos.to_s.ljust(3)} #{str}\r"
|
80
|
+
end
|
81
|
+
|
82
|
+
def format_signal_group_status
|
83
|
+
@signal_groups.map { |group| group.state }.join
|
84
|
+
end
|
85
|
+
|
86
|
+
def handle_command command_code, arg
|
87
|
+
case command_code
|
88
|
+
when 'M0001', 'M0002', 'M0003', 'M0004', 'M0005', 'M0006', 'M0007',
|
89
|
+
'M0012', 'M0013', 'M0014', 'M0015', 'M0016', 'M0017', 'M0018',
|
90
|
+
'M0019', 'M0020', 'M0021', 'M0022',
|
91
|
+
'M0103', 'M0104'
|
92
|
+
|
93
|
+
return send("handle_#{command_code.downcase}", arg)
|
94
|
+
else
|
95
|
+
raise UnknownCommand.new "Unknown command #{command_code}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def handle_m0001 arg
|
100
|
+
@node.verify_security_code 2, arg['securityCode']
|
101
|
+
switch_mode arg['status']
|
102
|
+
end
|
103
|
+
|
104
|
+
def handle_m0002 arg
|
105
|
+
@node.verify_security_code 2, arg['securityCode']
|
106
|
+
if RSMP::Tlc.from_rsmp_bool(arg['status'])
|
107
|
+
switch_plan arg['timeplan']
|
108
|
+
else
|
109
|
+
switch_plan 0 # TODO use clock/calender
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def handle_m0003 arg
|
114
|
+
@node.verify_security_code 2, arg['securityCode']
|
115
|
+
@traffic_situation = arg['traficsituation'].to_i
|
116
|
+
end
|
117
|
+
|
118
|
+
def handle_m0004 arg
|
119
|
+
@node.verify_security_code 2, arg['securityCode']
|
120
|
+
# don't restart immeediately, since we need to first send command response
|
121
|
+
# instead, defer an action, which will be handled by the TLC site
|
122
|
+
log "Sheduling restart of TLC", level: :info
|
123
|
+
@node.defer :restart
|
124
|
+
end
|
125
|
+
|
126
|
+
def handle_m0005 arg
|
127
|
+
@node.verify_security_code 2, arg['securityCode']
|
128
|
+
@emergency_route = (arg['status'] == 'True')
|
129
|
+
@emergency_route_number = arg['emergencyroute'].to_i
|
130
|
+
|
131
|
+
if @emergency_route
|
132
|
+
log "Switching to emergency route #{@emergency_route_number}", level: :info
|
133
|
+
else
|
134
|
+
log "Switching off emergency route", level: :info
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def handle_m0006 arg
|
139
|
+
@node.verify_security_code 2, arg['securityCode']
|
140
|
+
input = arg['input'].to_i
|
141
|
+
idx = input - 1
|
142
|
+
return unless idx>=0 && input<@num_inputs # TODO should NotAck
|
143
|
+
@input_activations[idx] = (arg['status']=='True' ? '1' : '0')
|
144
|
+
result = @input_activations[idx]=='1' || @inputs[idx]=='1'
|
145
|
+
@input_results[idx] = (result ? '1' : '0')
|
146
|
+
if @input_activations[idx]
|
147
|
+
log "Activate input #{idx}", level: :info
|
148
|
+
else
|
149
|
+
log "Deactivate input #{idx}", level: :info
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def handle_m0007 arg
|
154
|
+
@node.verify_security_code 2, arg['securityCode']
|
155
|
+
set_fixed_time_control arg['status']
|
156
|
+
end
|
157
|
+
|
158
|
+
def handle_m0012 arg
|
159
|
+
@node.verify_security_code 2, arg['securityCode']
|
160
|
+
end
|
161
|
+
|
162
|
+
def handle_m0013 arg
|
163
|
+
@node.verify_security_code 2, arg['securityCode']
|
164
|
+
end
|
165
|
+
|
166
|
+
def handle_m0014 arg
|
167
|
+
@node.verify_security_code 2, arg['securityCode']
|
168
|
+
end
|
169
|
+
|
170
|
+
def handle_m0015 arg
|
171
|
+
@node.verify_security_code 2, arg['securityCode']
|
172
|
+
end
|
173
|
+
|
174
|
+
def handle_m0016 arg
|
175
|
+
@node.verify_security_code 2, arg['securityCode']
|
176
|
+
end
|
177
|
+
|
178
|
+
def handle_m0017 arg
|
179
|
+
@node.verify_security_code 2, arg['securityCode']
|
180
|
+
end
|
181
|
+
|
182
|
+
def handle_m0018 arg
|
183
|
+
@node.verify_security_code 2, arg['securityCode']
|
184
|
+
end
|
185
|
+
|
186
|
+
def handle_m0019 arg
|
187
|
+
@node.verify_security_code 2, arg['securityCode']
|
188
|
+
end
|
189
|
+
|
190
|
+
def handle_m0020 arg
|
191
|
+
@node.verify_security_code 2, arg['securityCode']
|
192
|
+
end
|
193
|
+
|
194
|
+
def handle_m0021 arg
|
195
|
+
@node.verify_security_code 2, arg['securityCode']
|
196
|
+
end
|
197
|
+
|
198
|
+
def handle_m0103 arg
|
199
|
+
level = {'Level1'=>1,'Level2'=>2}[arg['status']]
|
200
|
+
@node.change_security_code level, arg['oldSecurityCode'], arg['newSecurityCode']
|
201
|
+
end
|
202
|
+
|
203
|
+
def handle_m0104 arg
|
204
|
+
@node.verify_security_code 1, arg['securityCode']
|
205
|
+
end
|
206
|
+
|
207
|
+
def set_input i, value
|
208
|
+
return unless i>=0 && i<@num_inputs
|
209
|
+
@inputs[i] = (arg['value'] ? '1' : '0')
|
210
|
+
end
|
211
|
+
|
212
|
+
def set_fixed_time_control status
|
213
|
+
@fixed_time_control = status
|
214
|
+
end
|
215
|
+
|
216
|
+
def switch_plan plan
|
217
|
+
plan_nr = plan.to_i
|
218
|
+
if plan_nr == 0
|
219
|
+
log "Switching to plan selection by time table", level: :info
|
220
|
+
else
|
221
|
+
log "Switching to plan #{plan_nr}", level: :info
|
222
|
+
end
|
223
|
+
@plan = plan_nr
|
224
|
+
end
|
225
|
+
|
226
|
+
def switch_mode mode
|
227
|
+
log "Switching to mode #{mode}", level: :info
|
228
|
+
case mode
|
229
|
+
when 'NormalControl'
|
230
|
+
@yellow_flash = false
|
231
|
+
@dark_mode = false
|
232
|
+
when 'YellowFlash'
|
233
|
+
@yellow_flash = true
|
234
|
+
@dark_mode = false
|
235
|
+
when 'Dark'
|
236
|
+
@yellow_flash = false
|
237
|
+
@dark_mode = true
|
238
|
+
end
|
239
|
+
mode
|
240
|
+
end
|
241
|
+
|
242
|
+
def get_status code, name=nil
|
243
|
+
case code
|
244
|
+
when 'S0001', 'S0002', 'S0003', 'S0004', 'S0005', 'S0006', 'S0007',
|
245
|
+
'S0008', 'S0009', 'S0010', 'S0011', 'S0012', 'S0013', 'S0014',
|
246
|
+
'S0015', 'S0016', 'S0017', 'S0018', 'S0019', 'S0020', 'S0021',
|
247
|
+
'S0022', 'S0023', 'S0024', 'S0026', 'S0027', 'S0028',
|
248
|
+
'S0029', 'S0030', 'S0031',
|
249
|
+
'S0091', 'S0092', 'S0095', 'S0096', 'S0097',
|
250
|
+
'S0205', 'S0206', 'S0207', 'S0208'
|
251
|
+
return send("handle_#{code.downcase}", code, name)
|
252
|
+
else
|
253
|
+
raise InvalidMessage.new "unknown status code #{code}"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def handle_s0001 status_code, status_name=nil
|
258
|
+
case status_name
|
259
|
+
when 'signalgroupstatus'
|
260
|
+
RSMP::Tlc.make_status format_signal_group_status
|
261
|
+
when 'cyclecounter'
|
262
|
+
RSMP::Tlc.make_status @pos.to_s
|
263
|
+
when 'basecyclecounter'
|
264
|
+
RSMP::Tlc.make_status @pos.to_s
|
265
|
+
when 'stage'
|
266
|
+
RSMP::Tlc.make_status 0.to_s
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def handle_s0002 status_code, status_name=nil
|
271
|
+
case status_name
|
272
|
+
when 'detectorlogicstatus'
|
273
|
+
RSMP::Tlc.make_status @detector_logics.map { |dl| dl.forced ? '1' : '0' }.join
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def handle_s0003 status_code, status_name=nil
|
278
|
+
case status_name
|
279
|
+
when 'inputstatus'
|
280
|
+
RSMP::Tlc.make_status @input_results
|
281
|
+
when 'extendedinputstatus'
|
282
|
+
RSMP::Tlc.make_status 0.to_s
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def handle_s0004 status_code, status_name=nil
|
287
|
+
case status_name
|
288
|
+
when 'outputstatus'
|
289
|
+
RSMP::Tlc.make_status 0
|
290
|
+
when 'extendedoutputstatus'
|
291
|
+
RSMP::Tlc.make_status 0
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def handle_s0005 status_code, status_name=nil
|
296
|
+
case status_name
|
297
|
+
when 'status'
|
298
|
+
RSMP::Tlc.make_status @is_starting
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def handle_s0006 status_code, status_name=nil
|
303
|
+
case status_name
|
304
|
+
when 'status'
|
305
|
+
RSMP::Tlc.make_status @emergency_route
|
306
|
+
when 'emergencystage'
|
307
|
+
RSMP::Tlc.make_status @emergency_route_number
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def handle_s0007 status_code, status_name=nil
|
312
|
+
case status_name
|
313
|
+
when 'intersection'
|
314
|
+
RSMP::Tlc.make_status @intersection
|
315
|
+
when 'status'
|
316
|
+
RSMP::Tlc.make_status !@dark_mode
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def handle_s0008 status_code, status_name=nil
|
321
|
+
case status_name
|
322
|
+
when 'intersection'
|
323
|
+
RSMP::Tlc.make_status @intersection
|
324
|
+
when 'status'
|
325
|
+
RSMP::Tlc.make_status @manual_control
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def handle_s0009 status_code, status_name=nil
|
330
|
+
case status_name
|
331
|
+
when 'intersection'
|
332
|
+
RSMP::Tlc.make_status @intersection
|
333
|
+
when 'status'
|
334
|
+
RSMP::Tlc.make_status @fixed_time_control
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def handle_s0010 status_code, status_name=nil
|
339
|
+
case status_name
|
340
|
+
when 'intersection'
|
341
|
+
RSMP::Tlc.make_status @intersection
|
342
|
+
when 'status'
|
343
|
+
RSMP::Tlc.make_status @isolated_control
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def handle_s0011 status_code, status_name=nil
|
348
|
+
case status_name
|
349
|
+
when 'intersection'
|
350
|
+
RSMP::Tlc.make_status @intersection
|
351
|
+
when 'status'
|
352
|
+
RSMP::Tlc.make_status @yellow_flash
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def handle_s0012 status_code, status_name=nil
|
357
|
+
case status_name
|
358
|
+
when 'intersection'
|
359
|
+
RSMP::Tlc.make_status @intersection
|
360
|
+
when 'status'
|
361
|
+
RSMP::Tlc.make_status @all_red
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def handle_s0013 status_code, status_name=nil
|
366
|
+
case status_name
|
367
|
+
when 'intersection'
|
368
|
+
RSMP::Tlc.make_status @intersection
|
369
|
+
when 'status'
|
370
|
+
RSMP::Tlc.make_status @police_key
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def handle_s0014 status_code, status_name=nil
|
375
|
+
case status_name
|
376
|
+
when 'status'
|
377
|
+
RSMP::Tlc.make_status @plan
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def handle_s0015 status_code, status_name=nil
|
382
|
+
case status_name
|
383
|
+
when 'status'
|
384
|
+
RSMP::Tlc.make_status @traffic_situation
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def handle_s0016 status_code, status_name=nil
|
389
|
+
case status_name
|
390
|
+
when 'number'
|
391
|
+
RSMP::Tlc.make_status @detector_logics.size
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def handle_s0017 status_code, status_name=nil
|
396
|
+
case status_name
|
397
|
+
when 'number'
|
398
|
+
RSMP::Tlc.make_status @signal_groups.size
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def handle_s0018 status_code, status_name=nil
|
403
|
+
case status_name
|
404
|
+
when 'number'
|
405
|
+
RSMP::Tlc.make_status @plans.size
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def handle_s0019 status_code, status_name=nil
|
410
|
+
case status_name
|
411
|
+
when 'number'
|
412
|
+
RSMP::Tlc.make_status @num_traffic_situations
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def handle_s0020 status_code, status_name=nil
|
417
|
+
case status_name
|
418
|
+
when 'intersection'
|
419
|
+
RSMP::Tlc.make_status @intersection
|
420
|
+
when 'controlmode'
|
421
|
+
RSMP::Tlc.make_status @control_mode
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def handle_s0021 status_code, status_name=nil
|
426
|
+
case status_name
|
427
|
+
when 'detectorlogics'
|
428
|
+
RSMP::Tlc.make_status @detector_logics.map { |logic| logic.forced=='True' ? '1' : '0'}.join
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def handle_s0022 status_code, status_name=nil
|
433
|
+
case status_name
|
434
|
+
when 'status'
|
435
|
+
RSMP::Tlc.make_status '1'
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def handle_s0023 status_code, status_name=nil
|
440
|
+
case status_name
|
441
|
+
when 'status'
|
442
|
+
RSMP::Tlc.make_status '1-1-0'
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def handle_s0024 status_code, status_name=nil
|
447
|
+
case status_name
|
448
|
+
when 'status'
|
449
|
+
RSMP::Tlc.make_status '1-0'
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
def handle_s0026 status_code, status_name=nil
|
454
|
+
case status_name
|
455
|
+
when 'status'
|
456
|
+
RSMP::Tlc.make_status '0-00'
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def handle_s0027 status_code, status_name=nil
|
461
|
+
case status_name
|
462
|
+
when 'status'
|
463
|
+
RSMP::Tlc.make_status '00-00-00-00'
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def handle_s0028 status_code, status_name=nil
|
468
|
+
case status_name
|
469
|
+
when 'status'
|
470
|
+
RSMP::Tlc.make_status '00-00'
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
def handle_s0029 status_code, status_name=nil
|
475
|
+
case status_name
|
476
|
+
when 'status'
|
477
|
+
RSMP::Tlc.make_status ''
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
def handle_s0030 status_code, status_name=nil
|
482
|
+
case status_name
|
483
|
+
when 'status'
|
484
|
+
RSMP::Tlc.make_status ''
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
def handle_s0031 status_code, status_name=nil
|
489
|
+
case status_name
|
490
|
+
when 'status'
|
491
|
+
RSMP::Tlc.make_status ''
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
def handle_s0091 status_code, status_name=nil
|
496
|
+
case status_name
|
497
|
+
when 'user'
|
498
|
+
RSMP::Tlc.make_status 'nobody'
|
499
|
+
when 'status'
|
500
|
+
RSMP::Tlc.make_status 'logout'
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
def handle_s0092 status_code, status_name=nil
|
505
|
+
case status_name
|
506
|
+
when 'user'
|
507
|
+
RSMP::Tlc.make_status 'nobody'
|
508
|
+
when 'status'
|
509
|
+
RSMP::Tlc.make_status 'logout'
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def handle_s0095 status_code, status_name=nil
|
514
|
+
case status_name
|
515
|
+
when 'status'
|
516
|
+
RSMP::Tlc.make_status RSMP::VERSION
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
def handle_s0096 status_code, status_name=nil
|
521
|
+
case status_name
|
522
|
+
when 'year'
|
523
|
+
RSMP::Tlc.make_status RSMP.now_object.year.to_s.rjust(4, "0")
|
524
|
+
when 'month'
|
525
|
+
RSMP::Tlc.make_status RSMP.now_object.month.to_s.rjust(2, "0")
|
526
|
+
when 'day'
|
527
|
+
RSMP::Tlc.make_status RSMP.now_object.day.to_s.rjust(2, "0")
|
528
|
+
when 'hour'
|
529
|
+
RSMP::Tlc.make_status RSMP.now_object.hour.to_s.rjust(2, "0")
|
530
|
+
when 'minute'
|
531
|
+
RSMP::Tlc.make_status RSMP.now_object.min.to_s.rjust(2, "0")
|
532
|
+
when 'second'
|
533
|
+
RSMP::Tlc.make_status RSMP.now_object.sec.to_s.rjust(2, "0")
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def handle_s0097 status_code, status_name=nil
|
538
|
+
case status_name
|
539
|
+
when 'version'
|
540
|
+
RSMP::Tlc.make_status '1'
|
541
|
+
when 'hash'
|
542
|
+
RSMP::Tlc.make_status '1'
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
def handle_s0205 status_code, status_name=nil
|
547
|
+
case status_name
|
548
|
+
when 'start'
|
549
|
+
RSMP::Tlc.make_status RSMP.now_string
|
550
|
+
when 'vehicles'
|
551
|
+
RSMP::Tlc.make_status 0
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
def handle_s0206 status_code, status_name=nil
|
556
|
+
case status_name
|
557
|
+
when 'start'
|
558
|
+
RSMP::Tlc.make_status RSMP.now_string
|
559
|
+
when 'speed'
|
560
|
+
RSMP::Tlc.make_status 0
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
def handle_s0207 status_code, status_name=nil
|
565
|
+
case status_name
|
566
|
+
when 'start'
|
567
|
+
RSMP::Tlc.make_status RSMP.now_string
|
568
|
+
when 'occupancy'
|
569
|
+
RSMP::Tlc.make_status 0
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
def handle_s0208 status_code, status_name=nil
|
574
|
+
case status_name
|
575
|
+
when 'start'
|
576
|
+
RSMP::Tlc.make_status RSMP.now_string
|
577
|
+
when 'P'
|
578
|
+
RSMP::Tlc.make_status 0
|
579
|
+
when 'PS'
|
580
|
+
RSMP::Tlc.make_status 0
|
581
|
+
when 'L'
|
582
|
+
RSMP::Tlc.make_status 0
|
583
|
+
when 'LS'
|
584
|
+
RSMP::Tlc.make_status 0
|
585
|
+
when 'B'
|
586
|
+
RSMP::Tlc.make_status 0
|
587
|
+
when 'SP'
|
588
|
+
RSMP::Tlc.make_status 0
|
589
|
+
when 'MC'
|
590
|
+
RSMP::Tlc.make_status 0
|
591
|
+
when 'C'
|
592
|
+
RSMP::Tlc.make_status 0
|
593
|
+
when 'F'
|
594
|
+
RSMP::Tlc.make_status 0
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|
599
|
+
|
600
|
+
class SignalGroup < Component
|
601
|
+
attr_reader :plan, :state
|
602
|
+
|
603
|
+
def initialize node:, id:, plan:
|
604
|
+
super node: node, id: id, grouped: false
|
605
|
+
@plan = plan
|
606
|
+
move 0
|
607
|
+
end
|
608
|
+
|
609
|
+
def get_state pos
|
610
|
+
if pos > @plan.length
|
611
|
+
'.'
|
612
|
+
else
|
613
|
+
@plan[pos]
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
def move pos
|
618
|
+
@state = get_state pos
|
619
|
+
end
|
620
|
+
|
621
|
+
def handle_command command_code, arg
|
622
|
+
case command_code
|
623
|
+
when 'M0010', 'M0011'
|
624
|
+
return send("handle_#{command_code.downcase}", arg)
|
625
|
+
else
|
626
|
+
raise UnknownCommand.new "Unknown command #{command_code}"
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
# Start of signal group. Orders a signal group to green
|
631
|
+
def handle_m0010 arg
|
632
|
+
@node.verify_security_code 2, arg['securityCode']
|
633
|
+
if RSMP::Tlc.from_rsmp_bool arg['status']
|
634
|
+
log "Start signal group #{c_id}, go to green", level: :info
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
# Stop of signal group. Orders a signal group to red
|
639
|
+
def handle_m0011 arg
|
640
|
+
@node.verify_security_code 2, arg['securityCode']
|
641
|
+
if RSMP::Tlc.from_rsmp_bool arg['status']
|
642
|
+
log "Stop signal group #{c_id}, go to red", level: :info
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
def get_status code, name=nil
|
647
|
+
case code
|
648
|
+
when 'S0025'
|
649
|
+
return send("handle_#{code.downcase}", code, name)
|
650
|
+
else
|
651
|
+
raise InvalidMessage.new "unknown status code #{code}"
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
def handle_s0025 status_code, status_name=nil
|
656
|
+
case status_name
|
657
|
+
when 'minToGEstimate'
|
658
|
+
RSMP::Tlc.make_status RSMP.now_string
|
659
|
+
when 'maxToGEstimate'
|
660
|
+
RSMP::Tlc.make_status RSMP.now_string
|
661
|
+
when 'likelyToGEstimate'
|
662
|
+
RSMP::Tlc.make_status RSMP.now_string
|
663
|
+
when 'ToGConfidence'
|
664
|
+
RSMP::Tlc.make_status 0
|
665
|
+
when 'minToREstimate'
|
666
|
+
RSMP::Tlc.make_status RSMP.now_string
|
667
|
+
when 'maxToREstimate'
|
668
|
+
RSMP::Tlc.make_status RSMP.now_string
|
669
|
+
when 'likelyToREstimate'
|
670
|
+
RSMP::Tlc.make_status RSMP.now_string
|
671
|
+
when 'ToRConfidence'
|
672
|
+
RSMP::Tlc.make_status 0
|
673
|
+
end
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
class DetectorLogic < Component
|
678
|
+
attr_reader :status, :forced, :value
|
679
|
+
|
680
|
+
def initialize node:, id:
|
681
|
+
super node: node, id: id, grouped: false
|
682
|
+
@forced = 0
|
683
|
+
@value = 0
|
684
|
+
end
|
685
|
+
|
686
|
+
def get_status code, name=nil
|
687
|
+
case code
|
688
|
+
when 'S0201', 'S0202', 'S0203', 'S0204'
|
689
|
+
return send("handle_#{code.downcase}", code, name)
|
690
|
+
else
|
691
|
+
raise InvalidMessage.new "unknown status code #{code}"
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
def handle_s0201 status_code, status_name=nil
|
696
|
+
case status_name
|
697
|
+
when 'starttime'
|
698
|
+
RSMP::Tlc.make_status RSMP.now_string
|
699
|
+
when 'vehicles'
|
700
|
+
RSMP::Tlc.make_status 0
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
def handle_s0202 status_code, status_name=nil
|
705
|
+
case status_name
|
706
|
+
when 'starttime'
|
707
|
+
RSMP::Tlc.make_status RSMP.now_string
|
708
|
+
when 'speed'
|
709
|
+
RSMP::Tlc.make_status 0
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
def handle_s0203 status_code, status_name=nil
|
714
|
+
case status_name
|
715
|
+
when 'starttime'
|
716
|
+
RSMP::Tlc.make_status RSMP.now_string
|
717
|
+
when 'occupancy'
|
718
|
+
RSMP::Tlc.make_status 0
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
def handle_s0204 status_code, status_name=nil
|
723
|
+
case status_name
|
724
|
+
when 'starttime'
|
725
|
+
RSMP::Tlc.make_status RSMP.now_string
|
726
|
+
when 'P'
|
727
|
+
RSMP::Tlc.make_status 0
|
728
|
+
when 'PS'
|
729
|
+
RSMP::Tlc.make_status 0
|
730
|
+
when 'L'
|
731
|
+
RSMP::Tlc.make_status 0
|
732
|
+
when 'LS'
|
733
|
+
RSMP::Tlc.make_status 0
|
734
|
+
when 'B'
|
735
|
+
RSMP::Tlc.make_status 0
|
736
|
+
when 'SP'
|
737
|
+
RSMP::Tlc.make_status 0
|
738
|
+
when 'MC'
|
739
|
+
RSMP::Tlc.make_status 0
|
740
|
+
when 'C'
|
741
|
+
RSMP::Tlc.make_status 0
|
742
|
+
when 'F'
|
743
|
+
RSMP::Tlc.make_status 0
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
def handle_command command_code, arg
|
748
|
+
case command_code
|
749
|
+
when 'M0008'
|
750
|
+
handle_m0008 arg
|
751
|
+
else
|
752
|
+
raise UnknownCommand.new "Unknown command #{command_code}"
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
def handle_m0008 arg
|
757
|
+
@node.verify_security_code 2, arg['securityCode']
|
758
|
+
force_detector_logic arg['status']=='True', arg['value']='True'
|
759
|
+
arg
|
760
|
+
end
|
761
|
+
|
762
|
+
def force_detector_logic status, value
|
763
|
+
@forced = status
|
764
|
+
@value = value
|
765
|
+
end
|
766
|
+
|
767
|
+
end
|
768
|
+
|
769
|
+
class Tlc < Site
|
770
|
+
def initialize options={}
|
771
|
+
super options
|
772
|
+
@sxl = 'traffic_light_controller'
|
773
|
+
@security_codes = options[:site_settings]['security_codes']
|
774
|
+
@interval = options[:site_settings]['interval'] || 1
|
775
|
+
unless @main
|
776
|
+
raise ConfigurationError.new "TLC must have a main component"
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
def build_component id:, type:, settings:{}
|
781
|
+
component = case type
|
782
|
+
when 'main'
|
783
|
+
@main = TrafficController.new node: self, id: id, cycle_time: settings['cycle_time']
|
784
|
+
when 'signal_group'
|
785
|
+
group = SignalGroup.new node: self, id: id, plan: settings['plan']
|
786
|
+
@main.add_signal_group group
|
787
|
+
group
|
788
|
+
when 'detector_logic'
|
789
|
+
logic = DetectorLogic.new node: self, id: id
|
790
|
+
@main.add_detector_logic logic
|
791
|
+
logic
|
792
|
+
end
|
793
|
+
end
|
794
|
+
|
795
|
+
def start_action
|
796
|
+
super
|
797
|
+
start_timer
|
798
|
+
end
|
799
|
+
|
800
|
+
def start_timer
|
801
|
+
task_name = "tlc timer"
|
802
|
+
log "Starting #{task_name} with interval #{@interval} seconds", level: :debug
|
803
|
+
|
804
|
+
@timer = @task.async do |task|
|
805
|
+
task.annotate task_name
|
806
|
+
next_time = Time.now.to_f
|
807
|
+
loop do
|
808
|
+
begin
|
809
|
+
now = RSMP.now_object
|
810
|
+
timer(now)
|
811
|
+
rescue EOFError => e
|
812
|
+
log "TLC timer: Connection closed: #{e}", level: :warning
|
813
|
+
rescue IOError => e
|
814
|
+
log "TLC timer: IOError", level: :warning
|
815
|
+
rescue Errno::ECONNRESET
|
816
|
+
log "TLC timer: Connection reset by peer", level: :warning
|
817
|
+
rescue Errno::EPIPE => e
|
818
|
+
log "TLC timer: Broken pipe", level: :warning
|
819
|
+
rescue StandardError => e
|
820
|
+
log "TLC timer: #{e}", level: :debug
|
821
|
+
ensure
|
822
|
+
# adjust sleep duration to avoid drift. so wake up always happens on the
|
823
|
+
# same fractional second.
|
824
|
+
# note that Time.now is not monotonic. If the clock si changed,
|
825
|
+
# either manaully or via NTP, the sleep interval might jump.
|
826
|
+
# an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
|
827
|
+
# to get the current time. this ensures a constant interval, but
|
828
|
+
# if the clock is changed, the wake up would then happen on a different
|
829
|
+
# fractional second
|
830
|
+
next_time += @interval
|
831
|
+
duration = next_time - Time.now.to_f
|
832
|
+
task.sleep duration
|
833
|
+
end
|
834
|
+
end
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
def timer now
|
839
|
+
return unless @main
|
840
|
+
@main.timer now
|
841
|
+
end
|
842
|
+
|
843
|
+
def verify_security_code level, code
|
844
|
+
raise ArgumentError.new("Level must be 1-2, got #{level}") unless (1..2).include?(level)
|
845
|
+
if @security_codes[level] != code
|
846
|
+
raise MessageRejected.new("Wrong security code for level #{level}")
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
def change_security_code level, old_code, new_code
|
851
|
+
verify_security_code level, old_code
|
852
|
+
@security_codes[level] = new_code
|
853
|
+
end
|
854
|
+
|
855
|
+
def self.to_rmsp_bool bool
|
856
|
+
if bool
|
857
|
+
'True'
|
858
|
+
else
|
859
|
+
'False'
|
860
|
+
end
|
861
|
+
end
|
862
|
+
|
863
|
+
def self.from_rsmp_bool str
|
864
|
+
str == 'True'
|
865
|
+
end
|
866
|
+
|
867
|
+
def self.make_status value, q='recent'
|
868
|
+
case value
|
869
|
+
when true, false
|
870
|
+
return to_rmsp_bool(value), q
|
871
|
+
else
|
872
|
+
return value, q
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
def do_deferred item
|
877
|
+
case item
|
878
|
+
when :restart
|
879
|
+
log "Restarting TLC", level: :info
|
880
|
+
restart
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
884
|
+
end
|
885
|
+
end
|