rsmp 0.9.8 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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