rsmp 0.9.8 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/config/tlc.yaml +1 -0
- data/lib/rsmp/component.rb +22 -0
- data/lib/rsmp/site.rb +6 -0
- data/lib/rsmp/site_proxy.rb +1 -1
- data/lib/rsmp/supervisor_proxy.rb +6 -0
- data/lib/rsmp/tlc/inputs.rb +134 -0
- data/lib/rsmp/tlc/traffic_controller.rb +46 -52
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 966835905e623249647c1c09a2571ffe81e42a4d28b7f61630f11dbd886f5e60
|
4
|
+
data.tar.gz: 134c264fdbeddd2a2dd72613ccb96d9476f732ecff086ae274bb0bc25a381e5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '04027059289848f757e10b46b5cc9ba15ade51460cdaaa80d442945dd79a3300f676a593a6cc3ec29c945f9340664855cca3304188cbef99f89531820731004f'
|
7
|
+
data.tar.gz: a04881e95b77482897ccd746047847e3036953515d02539fb2988b1a743135ebf0d6c66d1f15c39c46d7d3bfd8c5df67568bddf58ac26df3fee5fca5f00d3dd4
|
data/Gemfile.lock
CHANGED
data/config/tlc.yaml
CHANGED
data/lib/rsmp/component.rb
CHANGED
@@ -77,6 +77,7 @@ module RSMP
|
|
77
77
|
raise UnknownStatus.new "Status #{status_code}/#{status_name} not implemented by #{self.class}"
|
78
78
|
end
|
79
79
|
|
80
|
+
# handle incoming alarm
|
80
81
|
def handle_alarm message
|
81
82
|
code = message.attribute('aCId')
|
82
83
|
previous = @alarms[code]
|
@@ -92,6 +93,27 @@ module RSMP
|
|
92
93
|
@alarms[code] = message
|
93
94
|
end
|
94
95
|
|
96
|
+
# set alarm
|
97
|
+
def send_alarm code:, status:
|
98
|
+
# TODO
|
99
|
+
# we need to manage the state of alarms internally (an ALarm class probably),
|
100
|
+
# and handle request from the supervisor to suspend and resume alarms etc.
|
101
|
+
# when this state changes, we then send an alarm message
|
102
|
+
alarm = Alarm.new(
|
103
|
+
'cId' => c_id,
|
104
|
+
'aTs' => @node.clock.to_s,
|
105
|
+
'aCId' => code,
|
106
|
+
'aSp' => 'Issue',
|
107
|
+
'ack' => 'Acknowledged',
|
108
|
+
'sS' => 'notSuspended',
|
109
|
+
'aS' => status,
|
110
|
+
'cat' => 'D',
|
111
|
+
'pri' => '2',
|
112
|
+
'rvs' => []
|
113
|
+
)
|
114
|
+
@node.alarm_changed self, alarm
|
115
|
+
end
|
116
|
+
|
95
117
|
# Handle an incoming status respone, by storing the values
|
96
118
|
def handle_status_response message
|
97
119
|
store_status message, check_repeated: false
|
data/lib/rsmp/site.rb
CHANGED
@@ -93,6 +93,12 @@ module RSMP
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
+
def alarm_changed component, alarm
|
97
|
+
@proxies.each do |proxy|
|
98
|
+
proxy.send_alarm component, alarm if proxy.ready?
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
96
102
|
def connect_to_supervisor task, supervisor_settings
|
97
103
|
proxy = build_proxy({
|
98
104
|
site: self,
|
data/lib/rsmp/site_proxy.rb
CHANGED
@@ -72,7 +72,7 @@ module RSMP
|
|
72
72
|
else
|
73
73
|
super message
|
74
74
|
end
|
75
|
-
rescue RSMP::RepeatedAlarmError, RSMP::RepeatedStatusError => e
|
75
|
+
rescue RSMP::RepeatedAlarmError, RSMP::RepeatedStatusError, RSMP::TimestampError => e
|
76
76
|
str = "Rejected #{message.type} message,"
|
77
77
|
dont_acknowledge message, str, "#{e}"
|
78
78
|
notify_error e.exception("#{str}#{e.message} #{message.json}")
|
@@ -172,6 +172,12 @@ module RSMP
|
|
172
172
|
end
|
173
173
|
end
|
174
174
|
|
175
|
+
def send_alarm component, alarm, options={}
|
176
|
+
send_and_optionally_collect alarm, options do |collect_options|
|
177
|
+
Collector.new self, collect_options.merge(task:@task, type: 'MessageAck')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
175
181
|
def process_aggregated_status message
|
176
182
|
se = message.attribute("se")
|
177
183
|
validate_aggregated_status(message,se) == false
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module RSMP
|
2
|
+
module TLC
|
3
|
+
# class that maintains the state of TLC inputs
|
4
|
+
# indexing is 1-based since that's how the RSMP messages are specified
|
5
|
+
class Inputs
|
6
|
+
attr_reader :size
|
7
|
+
|
8
|
+
def initialize size
|
9
|
+
@size = size
|
10
|
+
reset
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset
|
14
|
+
string_size = @size+1
|
15
|
+
@value = '0'*string_size
|
16
|
+
@forced = '0'*string_size
|
17
|
+
@forced_value = '0'*string_size
|
18
|
+
@actual = '0'*string_size
|
19
|
+
end
|
20
|
+
|
21
|
+
def set input, value
|
22
|
+
check_input input
|
23
|
+
report_change(input) do
|
24
|
+
@value[input] = to_digit value
|
25
|
+
update_actual input
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_forcing input, force=true, forced_value=true
|
30
|
+
check_input input
|
31
|
+
report_change(input) do
|
32
|
+
@forced[input] = to_digit force
|
33
|
+
@forced_value[input] = to_digit forced_value
|
34
|
+
update_actual input
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def force input, forced_value=true
|
39
|
+
report_change(input) do
|
40
|
+
set_forcing input, true, forced_value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def release input
|
45
|
+
report_change(input) do
|
46
|
+
set_forcing input, false, false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def value input
|
51
|
+
check_input input
|
52
|
+
from_digit @value[input]
|
53
|
+
end
|
54
|
+
|
55
|
+
def forced? input
|
56
|
+
check_input input
|
57
|
+
from_digit @forced[input]
|
58
|
+
end
|
59
|
+
|
60
|
+
def forced_value input
|
61
|
+
check_input input
|
62
|
+
from_digit @forced_value[input]
|
63
|
+
end
|
64
|
+
|
65
|
+
def actual input
|
66
|
+
check_input input
|
67
|
+
from_digit @actual[input]
|
68
|
+
end
|
69
|
+
|
70
|
+
def report input
|
71
|
+
{
|
72
|
+
value: value(input),
|
73
|
+
forced: forced?(input),
|
74
|
+
forced_value: forced_value(input),
|
75
|
+
actual:actual(input)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def value_string
|
80
|
+
@value[1..-1]
|
81
|
+
end
|
82
|
+
|
83
|
+
def value_string
|
84
|
+
@value[1..-1]
|
85
|
+
end
|
86
|
+
|
87
|
+
def forced_string
|
88
|
+
@forced[1..-1]
|
89
|
+
end
|
90
|
+
|
91
|
+
def forced_value_string
|
92
|
+
@forced[1..-1]
|
93
|
+
end
|
94
|
+
|
95
|
+
def actual_string
|
96
|
+
@actual[1..-1]
|
97
|
+
end
|
98
|
+
|
99
|
+
protected
|
100
|
+
|
101
|
+
def check_input input
|
102
|
+
raise ArgumentError.new("Input index #{input} must be in the range 1-#{@size}") if input<1 || input>@size
|
103
|
+
end
|
104
|
+
|
105
|
+
def from_digit input
|
106
|
+
input == '1'
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_digit input
|
110
|
+
input ? '1' : '0'
|
111
|
+
end
|
112
|
+
|
113
|
+
def update_actual input
|
114
|
+
if from_digit @forced[input]
|
115
|
+
@actual[input] = @forced_value[input]
|
116
|
+
else
|
117
|
+
@actual[input] = @value[input]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def report_change input, &block
|
122
|
+
before = @actual[input]
|
123
|
+
yield
|
124
|
+
if @actual[input] != before
|
125
|
+
from_digit @actual[input]
|
126
|
+
else
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
@@ -19,12 +19,12 @@ module RSMP
|
|
19
19
|
@num_traffic_situations = 1
|
20
20
|
|
21
21
|
if inputs
|
22
|
-
|
22
|
+
num_inputs = inputs['total']
|
23
23
|
@input_programming = inputs['programming']
|
24
24
|
else
|
25
|
-
@num_inputs = 8
|
26
25
|
@input_programming = nil
|
27
26
|
end
|
27
|
+
@inputs = TLC::Inputs.new num_inputs || 8
|
28
28
|
|
29
29
|
@startup_sequence = startup_sequence
|
30
30
|
@live_output = live_output
|
@@ -48,25 +48,18 @@ module RSMP
|
|
48
48
|
|
49
49
|
def reset
|
50
50
|
reset_modes
|
51
|
-
|
52
51
|
@cycle_counter = 0
|
53
52
|
@plan = 1
|
54
53
|
@intersection = 0
|
55
54
|
@emergency_route = false
|
56
55
|
@emergency_route_number = 0
|
57
56
|
@traffic_situation = 0
|
58
|
-
|
59
|
-
@inputs = '0'*@num_inputs
|
60
|
-
@input_activations = '0'*@num_inputs
|
61
|
-
@input_forced = '0'*@num_inputs
|
62
|
-
@input_forced_values = '0'*@num_inputs
|
63
|
-
@input_results = '0'*@num_inputs
|
64
|
-
|
65
57
|
@day_time_table = {}
|
66
58
|
@startup_sequence_active = false
|
67
59
|
@startup_sequence_initiated_at = nil
|
68
60
|
@startup_sequence_pos = 0
|
69
61
|
@time_int = nil
|
62
|
+
@inputs.reset
|
70
63
|
end
|
71
64
|
|
72
65
|
def dark?
|
@@ -263,43 +256,36 @@ module RSMP
|
|
263
256
|
end
|
264
257
|
end
|
265
258
|
|
266
|
-
def
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
259
|
+
def input_logic input, change
|
260
|
+
return unless @input_programming && change != nil
|
261
|
+
actions = @input_programming[input]
|
262
|
+
return unless actions
|
263
|
+
if actions['raise']
|
264
|
+
alarm_code = actions['raise']
|
265
|
+
if change
|
266
|
+
log "Activating alarm #{alarm_code}, because input #{input} was activated", level: :info
|
267
|
+
send_alarm code:alarm_code, status:'Active'
|
268
|
+
else
|
269
|
+
log "Deactivating alarm #{alarm_code}, because input #{input} was deactivated", level: :info
|
270
|
+
send_alarm code:alarm_code, status:'inActive'
|
271
|
+
end
|
273
272
|
end
|
274
273
|
end
|
275
274
|
|
276
275
|
def handle_m0006 arg
|
277
276
|
@node.verify_security_code 2, arg['securityCode']
|
278
277
|
input = arg['input'].to_i
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
recompute_input idx
|
285
|
-
activate = @input_activations[idx] == '1'
|
286
|
-
if activate
|
278
|
+
status = string_to_bool arg['status']
|
279
|
+
unless input>=1 && input<=@inputs.size
|
280
|
+
raise MessageRejected.new("Input must be in the range 1-#{@inputs.size}")
|
281
|
+
end
|
282
|
+
if status
|
287
283
|
log "Activating input #{input}", level: :info
|
288
284
|
else
|
289
285
|
log "Deactivating input #{input}", level: :info
|
290
286
|
end
|
291
|
-
|
292
|
-
if
|
293
|
-
actions = @input_programming[input]
|
294
|
-
if actions && actions['raise']
|
295
|
-
alarm_code = actions['raise']
|
296
|
-
if activate
|
297
|
-
log "Activating alarm #{alarm_code}, due to input #{input} programming", level: :info
|
298
|
-
else
|
299
|
-
log "Deactivating alarm #{alarm_code}, due to input #{input} programming", level: :info
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
287
|
+
change = @inputs.set input, status
|
288
|
+
input_logic input, change if change != nil
|
303
289
|
end
|
304
290
|
|
305
291
|
def handle_m0007 arg
|
@@ -361,6 +347,17 @@ module RSMP
|
|
361
347
|
@node.verify_security_code 2, arg['securityCode']
|
362
348
|
end
|
363
349
|
|
350
|
+
def string_to_bool bool_str
|
351
|
+
case bool_str
|
352
|
+
when 'True'
|
353
|
+
true
|
354
|
+
when 'False'
|
355
|
+
false
|
356
|
+
else
|
357
|
+
raise RSMP::MessageRejected.new "Invalid boolean '#{bool}', must be 'True' or 'False'"
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
364
361
|
def bool_string_to_digit bool
|
365
362
|
case bool
|
366
363
|
when 'True'
|
@@ -379,21 +376,18 @@ module RSMP
|
|
379
376
|
def handle_m0019 arg
|
380
377
|
@node.verify_security_code 2, arg['securityCode']
|
381
378
|
input = arg['input'].to_i
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
end
|
387
|
-
|
388
|
-
|
389
|
-
@input_forced_values[idx] = bool_string_to_digit arg['inputValue']
|
390
|
-
end
|
391
|
-
recompute_input idx
|
392
|
-
if @input_forced[idx]
|
393
|
-
log "Forcing input #{idx+1} to #{@input_forced_values[idx]}, #{@input_results}", level: :info
|
379
|
+
force = string_to_bool arg['status']
|
380
|
+
forced_value = string_to_bool arg['inputValue']
|
381
|
+
unless input>=1 && input<=@inputs.size
|
382
|
+
raise MessageRejected.new("Input must be in the range 1-#{@inputs.size}")
|
383
|
+
end
|
384
|
+
if force
|
385
|
+
log "Forcing input #{input} to #{forced_value}", level: :info
|
394
386
|
else
|
395
|
-
log "Releasing input #{
|
387
|
+
log "Releasing input #{input}", level: :info
|
396
388
|
end
|
389
|
+
change = @inputs.set_forcing input, force, forced_value
|
390
|
+
input_logic input, change if change != nil
|
397
391
|
end
|
398
392
|
|
399
393
|
def handle_m0020 arg
|
@@ -503,7 +497,7 @@ module RSMP
|
|
503
497
|
def handle_s0003 status_code, status_name=nil
|
504
498
|
case status_name
|
505
499
|
when 'inputstatus'
|
506
|
-
TrafficControllerSite.make_status @
|
500
|
+
TrafficControllerSite.make_status @inputs.actual_string
|
507
501
|
when 'extendedinputstatus'
|
508
502
|
TrafficControllerSite.make_status 0.to_s
|
509
503
|
end
|
@@ -705,7 +699,7 @@ module RSMP
|
|
705
699
|
def handle_s0029 status_code, status_name=nil
|
706
700
|
case status_name
|
707
701
|
when 'status'
|
708
|
-
TrafficControllerSite.make_status @
|
702
|
+
TrafficControllerSite.make_status @inputs.forced_string
|
709
703
|
end
|
710
704
|
end
|
711
705
|
|
data/lib/rsmp/version.rb
CHANGED
data/lib/rsmp.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rsmp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emil Tin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -243,6 +243,7 @@ files:
|
|
243
243
|
- lib/rsmp/supervisor_proxy.rb
|
244
244
|
- lib/rsmp/task.rb
|
245
245
|
- lib/rsmp/tlc/detector_logic.rb
|
246
|
+
- lib/rsmp/tlc/inputs.rb
|
246
247
|
- lib/rsmp/tlc/signal_group.rb
|
247
248
|
- lib/rsmp/tlc/signal_plan.rb
|
248
249
|
- lib/rsmp/tlc/traffic_controller.rb
|