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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34b503622433b88389b62af2deca5e8c296fb305701ed1a8ed92b9b32dfe8893
4
- data.tar.gz: b76db428241ac087692925b5b259f55e40145a13f0436e56be4eb698688b6edb
3
+ metadata.gz: 966835905e623249647c1c09a2571ffe81e42a4d28b7f61630f11dbd886f5e60
4
+ data.tar.gz: 134c264fdbeddd2a2dd72613ccb96d9476f732ecff086ae274bb0bc25a381e5c
5
5
  SHA512:
6
- metadata.gz: bb0a905c3129dfa6960178fe427490950d10f4d04dd58ece46cea4ccfb318abffd18835b6a7b4cbefeb80103542be68c1a258687b97f632bbaa574cff409f73c
7
- data.tar.gz: 87e9b41facacfd9170874c1a19f5a3738261d55b8cd4eeecc53c51c5a540c0c6503e3756f8bdaa59dbf5eb7393c66ae5d61e93f77a58ad0bbe634a1886076061
6
+ metadata.gz: '04027059289848f757e10b46b5cc9ba15ade51460cdaaa80d442945dd79a3300f676a593a6cc3ec29c945f9340664855cca3304188cbef99f89531820731004f'
7
+ data.tar.gz: a04881e95b77482897ccd746047847e3036953515d02539fb2988b1a743135ebf0d6c66d1f15c39c46d7d3bfd8c5df67568bddf58ac26df3fee5fca5f00d3dd4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.9.8)
4
+ rsmp (0.10.0)
5
5
  async (~> 1.29.1)
6
6
  async-io (~> 1.32.2)
7
7
  colorize (~> 0.8.1)
data/config/tlc.yaml CHANGED
@@ -15,6 +15,7 @@ components:
15
15
  B2:
16
16
  detector_logic:
17
17
  DL1:
18
+ DL2:
18
19
  signal_plans:
19
20
  1:
20
21
  dynamic_bands:
@@ -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,
@@ -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
- @num_inputs = inputs['total'] || 8
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 recompute_input idx
267
- if @input_forced[idx] == '1'
268
- @input_results[idx] = @input_forced_values[idx]
269
- elsif @input_activations[idx]=='1'
270
- @input_results[idx] = '1'
271
- else
272
- @input_results[idx] = bool_to_digit( @inputs[idx]=='1' )
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
- unless input>0 && input<=@num_inputs
280
- raise MessageRejected.new("Input #{idx} is invalid, must be in the range 1-#{@num_inputs}")
281
- end
282
- idx = input - 1
283
- @input_activations[idx] = bool_string_to_digit arg['status']
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 @input_programming
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
- idx = input - 1
383
- unless idx>=0 && input<@num_inputs # TODO should NotAck
384
- log "Can't force input #{idx+1}, only have #{@num_inputs} inputs", level: :warning
385
- return
386
- end
387
- @input_forced[idx] = bool_string_to_digit arg['status']
388
- if @input_forced[idx]
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 #{idx+1}", level: :info
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 @input_results
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 @input_forced
702
+ TrafficControllerSite.make_status @inputs.forced_string
709
703
  end
710
704
  end
711
705
 
data/lib/rsmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = "0.9.8"
2
+ VERSION = "0.10.0"
3
3
  end
data/lib/rsmp.rb CHANGED
@@ -44,6 +44,7 @@ require 'rsmp/tlc/traffic_controller'
44
44
  require 'rsmp/tlc/detector_logic'
45
45
  require 'rsmp/tlc/signal_group'
46
46
  require 'rsmp/tlc/signal_plan'
47
+ require 'rsmp/tlc/inputs'
47
48
 
48
49
  require 'rsmp/convert/import/yaml'
49
50
  require 'rsmp/convert/export/json_schema'
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.9.8
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-10 00:00:00.000000000 Z
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