rsmp 0.3.4 → 0.3.8

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: f529bfbfcdd5fc6b0854c294e2412c5e7da10e2595a6a20ab0f6c65b7eb28964
4
- data.tar.gz: c8fb86566e623a27ec78784e3d0299f16d082edc87ad9438f2335a0c33657b39
3
+ metadata.gz: 5e0675af3b49828fa77194f4dbec427378e6cf79e8a4923ed452cf28a39052a2
4
+ data.tar.gz: d0a681be3741ac8a87c4d1b17d1db75218cae8d8df34a0f6365052ff59578235
5
5
  SHA512:
6
- metadata.gz: 58c5d44cb5d8a1f87b77d91823eaa76adfd437df955412ba42950e359d1b08b47acec78034f42524b72d474a6c7870e2e22f418973d634fc938650b13f95fc47
7
- data.tar.gz: 6006f41562af7ce3419c50895040fc0682026fa3c5b0db113875645fdd1de5286e25b73edc22f1d547a1daac50059532e7c4061cc5e3789d3a92c18c5e502aca
6
+ metadata.gz: 4012fd3e7f6c4c39c6f473ac09b115fbf67bf1651803c421e10f37e9e17b78b3d096dc7a4c36e125117f30daf5baf2e0e755bd83a06926a143ae7cb4bed91def
7
+ data.tar.gz: be15a086298d27b3cd8530685a8b654519540ac3d64f1aecd62399d513bc4c323f200e9d606c0afa28e1efc57a8d6e51c9f7dd77c4893ceb493c2613792c6567
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.3.3)
4
+ rsmp (0.3.8)
5
5
  async (~> 1.29.1)
6
6
  async-io (~> 1.32.1)
7
7
  colorize (~> 0.8.1)
@@ -13,7 +13,7 @@ module RSMP
13
13
  :rest,
14
14
  :not_connected ]
15
15
 
16
- def initialize node:, id:, grouped:
16
+ def initialize node:, id:, grouped: false
17
17
  @c_id = id
18
18
  @node = node
19
19
  @grouped = grouped
@@ -57,9 +57,6 @@ module RSMP
57
57
  def aggregated_status_changed options={}
58
58
  @node.aggregated_status_changed self, options
59
59
  end
60
-
61
- def alarm code:, status:
62
- end
63
60
 
64
61
  def log str, options
65
62
  @node.log str, options
@@ -73,5 +70,18 @@ module RSMP
73
70
  raise UnknownStatus.new "Status #{status_code}/#{status_name} not implemented by #{self.class}"
74
71
  end
75
72
 
73
+ def handle_alarm message
74
+ code = message.attribute('aCId')
75
+ alarm = @alarms[code]
76
+ if alarm
77
+ if alarm.differ? message
78
+ @alarms[code] = alarm
79
+ else
80
+ raise RepeatedAlarmError.new("no changes from previous alarm #{alarm.m_id_short}")
81
+ end
82
+ else
83
+ @alarms[code] = message
84
+ end
85
+ end
76
86
  end
77
87
  end
@@ -38,10 +38,23 @@ module RSMP
38
38
  Component.new id:id, node: self, grouped: type=='main'
39
39
  end
40
40
 
41
- def find_component component_id
41
+ def infer_component_type component_id
42
+ Component
43
+ end
44
+
45
+ def find_component component_id, build: true
42
46
  component = @components[component_id]
43
- raise UnknownComponent.new("Component #{component_id} not found") unless component
44
- component
47
+ return component if component
48
+ if build
49
+ inferred = infer_component_type component_id
50
+ component = inferred.new node: self, id: component_id
51
+ @components[ component_id] = component
52
+ class_name = component.class.name.split('::').last
53
+ log "Inferred #{class_name} component #{component_id}", level: :info
54
+ component
55
+ else
56
+ raise UnknownComponent.new("Component #{component_id} not found") unless component
57
+ end
45
58
  end
46
59
 
47
60
  end
data/lib/rsmp/error.rb CHANGED
@@ -52,4 +52,7 @@ module RSMP
52
52
 
53
53
  class ConfigurationError < Error
54
54
  end
55
+
56
+ class RepeatedAlarmError < Error
57
+ end
55
58
  end
data/lib/rsmp/message.rb CHANGED
@@ -36,7 +36,7 @@ module RSMP
36
36
  when "Watchdog"
37
37
  message = Watchdog.new attributes
38
38
  when "Alarm"
39
- message = Alarm.new attributes
39
+ message = self.build_alarm attributes
40
40
  when "CommandRequest"
41
41
  message = CommandRequest.new attributes
42
42
  when "CommandResponse"
@@ -59,6 +59,23 @@ module RSMP
59
59
  message
60
60
  end
61
61
 
62
+ def self.build_alarm attributes
63
+ case attributes["aSp"]
64
+ when 'Issue'
65
+ AlarmIssue.new attributes
66
+ when 'Request'
67
+ AlarmRequest.new attributes
68
+ when 'Acknowledge'
69
+ AlarmAcknowledged.new attributes
70
+ when 'Suspend'
71
+ AlarmSuspend.new attributes
72
+ when 'Resume'
73
+ AlarmResume.new attributes
74
+ else
75
+ Alarm.new attributes
76
+ end
77
+ end
78
+
62
79
  def type
63
80
  @attributes["type"]
64
81
  end
@@ -191,26 +208,64 @@ module RSMP
191
208
  def initialize attributes = {}
192
209
  super({
193
210
  "type" => "Alarm",
211
+ "ntsOId" => '',
212
+ "xNId" => '',
213
+ "xACId" => '',
214
+ "xNACId" => ''
194
215
  }.merge attributes)
195
216
  end
217
+
218
+ def differ? from
219
+ %w{aSp aCId ack aS sS aTs cat pri}.each do |key|
220
+ return true if attribute(key).downcase != from.attribute(key).downcase
221
+ end
222
+ return true if attribute('rvs') != from.attribute('rvs')
223
+ false
224
+ end
196
225
  end
197
226
 
198
- class AlarmRequest < Message
227
+ class AlarmIssue < Alarm
199
228
  def initialize attributes = {}
200
229
  super({
201
- "type" => "Alarm",
230
+ "aSp" => "Issue",
202
231
  }.merge attributes)
203
232
  end
204
233
  end
205
234
 
206
- class AlarmAcknowledged < Message
235
+ class AlarmRequest < Alarm
207
236
  def initialize attributes = {}
208
237
  super({
209
- "type" => "Alarm",
238
+ "aSp" => "Request",
239
+ }.merge attributes)
240
+ end
241
+ end
242
+
243
+ class AlarmAcknowledged < Alarm
244
+ def initialize attributes = {}
245
+ super({
246
+ "aSp" => "Acknowledge",
210
247
  }.merge attributes)
211
248
  end
212
249
  end
213
250
 
251
+ class AlarmSuspend < Alarm
252
+ def initialize attributes = {}
253
+ super({
254
+ "aSp" => "Suspend",
255
+ }.merge attributes)
256
+ end
257
+ end
258
+
259
+ class AlarmResume < Alarm
260
+ def initialize attributes = {}
261
+ super({
262
+ "aSp" => "Resume",
263
+ }.merge attributes)
264
+ end
265
+ end
266
+
267
+
268
+
214
269
  class Watchdog < Message
215
270
  def initialize attributes = {}
216
271
  super({
data/lib/rsmp/proxy.rb CHANGED
@@ -73,6 +73,11 @@ module RSMP
73
73
  @state == :ready
74
74
  end
75
75
 
76
+ def connected?
77
+ @state == :starting || @state == :ready
78
+ end
79
+
80
+
76
81
  def start
77
82
  set_state :starting
78
83
  end
@@ -118,7 +123,6 @@ module RSMP
118
123
  task.annotate "reader"
119
124
  @stream ||= Async::IO::Stream.new(@socket)
120
125
  @protocol ||= Async::IO::Protocol::Line.new(@stream,WRAPPING_DELIMITER) # rsmp messages are json terminated with a form-feed
121
-
122
126
  while json = @protocol.read_line
123
127
  beginning = Time.now
124
128
  message = process_packet json
@@ -267,12 +271,13 @@ module RSMP
267
271
  end
268
272
 
269
273
  def send_message message, reason=nil, validate: true
274
+ raise NotReady unless connected?
270
275
  raise IOError unless @protocol
271
276
  message.direction = :out
272
277
  message.generate_json
273
278
  message.validate get_schemas unless validate==false
274
- expect_acknowledgement message
275
279
  @protocol.write_lines message.json
280
+ expect_acknowledgement message
276
281
  notify message
277
282
  log_send message, reason
278
283
  rescue EOFError, IOError
@@ -303,9 +308,9 @@ module RSMP
303
308
  end
304
309
 
305
310
  def should_validate_ingoing_message? message
306
- return false unless @site_settings
311
+ return true unless @site_settings
307
312
  skip = @site_settings.dig('skip_validation')
308
- return false unless skip
313
+ return true unless skip
309
314
  klass = message.class.name.split('::').last
310
315
  !skip.include?(klass)
311
316
  end
data/lib/rsmp/site.rb CHANGED
@@ -83,7 +83,7 @@ module RSMP
83
83
 
84
84
  def aggregated_status_changed component, options={}
85
85
  @proxies.each do |proxy|
86
- proxy.send_aggregated_status component, options
86
+ proxy.send_aggregated_status component, options if proxy.ready?
87
87
  end
88
88
  end
89
89
 
@@ -125,17 +125,11 @@ module RSMP
125
125
  se = message.attribute("se")
126
126
  validate_aggregated_status(message,se) == false
127
127
  c_id = message.attributes["cId"]
128
- component = @components[c_id]
129
- if component == nil
130
- if @site_settings == nil || @site_settings['components'] == nil
131
- component = build_component(id:c_id, type:nil)
132
- @components[c_id] = component
133
- log "Adding component #{c_id} to site #{@site_id}", level: :info
134
- else
135
- reason = "component #{c_id} not found"
136
- dont_acknowledge message, "Ignoring #{message.type}:", reason
137
- return
138
- end
128
+ component = find_component c_id
129
+ unless component
130
+ reason = "component #{c_id} not found"
131
+ dont_acknowledge message, "Ignoring #{message.type}:", reason
132
+ return
139
133
  end
140
134
 
141
135
  component.set_aggregated_status_bools se
@@ -148,11 +142,17 @@ module RSMP
148
142
  end
149
143
 
150
144
  def process_alarm message
145
+ component = find_component message.attribute("cId")
146
+ status = ["ack","aS","sS"].map { |key| message.attribute(key) }.join(',')
147
+ component.handle_alarm message
151
148
  alarm_code = message.attribute("aCId")
152
149
  asp = message.attribute("aSp")
153
- status = ["ack","aS","sS"].map { |key| message.attribute(key) }.join(',')
154
150
  log "Received #{message.type}, #{alarm_code} #{asp} [#{status}]", message: message, level: :log
155
151
  acknowledge message
152
+ rescue RSMP::RepeatedAlarmError => e
153
+ str = "Rejected #{message.type} message, "
154
+ dont_acknowledge message, str, "#{e}"
155
+ notify_error e.exception("#{str}#{e.message} #{message.json}")
156
156
  end
157
157
 
158
158
  def version_acknowledged
@@ -241,13 +241,8 @@ module RSMP
241
241
 
242
242
  def send_alarm_acknowledgement component, alarm_code, options={}
243
243
  message = RSMP::AlarmAcknowledged.new({
244
- "ntsOId" => '',
245
- "xNId" => '',
246
244
  "cId" => component,
247
245
  "aCId" => alarm_code,
248
- "xACId" => '',
249
- "xNACId" => '',
250
- "aSp" => 'Acknowledge'
251
246
  })
252
247
  send_message message, validate: options[:validate]
253
248
  message
@@ -340,20 +335,12 @@ module RSMP
340
335
  @supervisor.notify_error e, options if @supervisor
341
336
  end
342
337
 
343
- def wait_for_alarm parent_task, options={}
344
- matching_alarm = nil
345
- message = collect(parent_task,options.merge(type: "Alarm", with_message: true, num: 1)) do |message|
346
- # TODO check components
347
- matching_alarm = nil
348
- alarm = message
338
+ def collect_alarms parent_task, options={}
339
+ collect(parent_task,options.merge(type: "Alarm")) do |alarm|
349
340
  next if options[:aCId] && options[:aCId] != alarm.attribute("aCId")
350
341
  next if options[:aSp] && options[:aSp] != alarm.attribute("aSp")
351
342
  next if options[:aS] && options[:aS] != alarm.attribute("aS")
352
- matching_alarm = alarm
353
- break
354
- end
355
- if item
356
- { message: message, status: matching_alarm }
343
+ true
357
344
  end
358
345
  end
359
346
 
@@ -363,16 +363,6 @@ module RSMP
363
363
  end
364
364
  end
365
365
 
366
- def send_alarm
367
- message = Alarm.new({
368
- "aSTS"=>clock.to_s,
369
- "fP"=>nil,
370
- "fS"=>nil,
371
- "se"=>@site.aggregated_status_bools
372
- })
373
- send_message message
374
- end
375
-
376
366
  def sxl_version
377
367
  @site_settings['sxl_version']
378
368
  end
data/lib/rsmp/tlc.rb CHANGED
@@ -5,7 +5,7 @@ module RSMP
5
5
  class TrafficController < Component
6
6
  attr_reader :pos, :cycle_time
7
7
 
8
- def initialize node:, id:, cycle_time:
8
+ def initialize node:, id:, cycle_time: 10
9
9
  super node: node, id: id, grouped: true
10
10
  @signal_groups = []
11
11
  @detector_logics = []
@@ -625,13 +625,15 @@ module RSMP
625
625
  class SignalGroup < Component
626
626
  attr_reader :plan, :state
627
627
 
628
- def initialize node:, id:, plan:
628
+ # plan is a string, with each character representing a signal phase at a particular second in the cycle
629
+ def initialize node:, id:, plan: nil
629
630
  super node: node, id: id, grouped: false
630
631
  @plan = plan
631
632
  move 0
632
633
  end
633
634
 
634
635
  def get_state pos
636
+ return 'a' unless @plan # if no plan, use phase a, which means disabled/dark
635
637
  if pos > @plan.length
636
638
  '.'
637
639
  else
data/lib/rsmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = "0.3.4"
2
+ VERSION = "0.3.8"
3
3
  end
data/lib/rsmp.rb CHANGED
@@ -18,7 +18,6 @@ require 'rsmp/node'
18
18
  require 'rsmp/supervisor'
19
19
  require 'rsmp/components'
20
20
  require 'rsmp/notifier'
21
-
22
21
  require 'rsmp/listener'
23
22
  require 'rsmp/collector'
24
23
  require 'rsmp/matcher'
data/test.rb ADDED
@@ -0,0 +1,27 @@
1
+ class A
2
+ def go &block
3
+ @block = block # block will be converted automatically to a Proc
4
+ indirect
5
+ end
6
+
7
+ def call
8
+ @block.call
9
+ end
10
+
11
+ def indirect
12
+ call
13
+ end
14
+
15
+ end
16
+
17
+ a = A.new
18
+
19
+ a.go do
20
+ break # this is ok. break causes the block to exit, and the encasing method to return - go() will exit
21
+ end
22
+
23
+ # this raises an error. the block we passed to go() will be called again, and it tries to break
24
+ # but we're not inside a method we can exit from
25
+
26
+
27
+ a.indirect
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.3.4
4
+ version: 0.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emil Tin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-18 00:00:00.000000000 Z
11
+ date: 2021-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -203,7 +203,6 @@ files:
203
203
  - documentation/message_distribution.md
204
204
  - exe/rsmp
205
205
  - lib/rsmp.rb
206
- - lib/rsmp/alarm.rb
207
206
  - lib/rsmp/archive.rb
208
207
  - lib/rsmp/cli.rb
209
208
  - lib/rsmp/collector.rb
@@ -232,6 +231,7 @@ files:
232
231
  - lib/rsmp/version.rb
233
232
  - lib/rsmp/wait.rb
234
233
  - rsmp.gemspec
234
+ - test.rb
235
235
  homepage: https://github.com/rsmp-nordic/rsmp
236
236
  licenses:
237
237
  - MIT
@@ -255,7 +255,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
255
255
  - !ruby/object:Gem::Version
256
256
  version: '0'
257
257
  requirements: []
258
- rubygems_version: 3.2.15
258
+ rubygems_version: 3.2.26
259
259
  signing_key:
260
260
  specification_version: 4
261
261
  summary: RoadSide Message Protocol (RSMP) library.
data/lib/rsmp/alarm.rb DELETED
@@ -1,15 +0,0 @@
1
- # RSMP Alarm. Manages the various states an alarm can be in.
2
-
3
- module RSMP
4
- class Alarm
5
-
6
- def initialize code: code, blocked: blocked=false, suspended: suspended=false, acknowledged: acknowledged=false
7
- @code = code
8
- @code = code
9
- @blocked = blocked
10
- @suspended = suspended
11
- @acknowledged = acknowledged
12
- end
13
-
14
- end
15
- end