rsmp 0.3.4 → 0.3.8

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: 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