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