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