rsmp 0.12.3 → 0.13.2

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: d97c24017cb23ab7159bebda96f262f50813d902365e2fa1fabeb90d4771df01
4
- data.tar.gz: fde070792556ef05d68ebfe5d7757d696f81921d75e64ced80f2a1c4878d39ef
3
+ metadata.gz: dfc37b07159cf717f60f293434f6fda0ff6935ad209967b7d5ddb025a9794600
4
+ data.tar.gz: 5c5e6b1e7728bdffd50776e95a7beb0fd6261dbb58b4ec3f530f7b33ee15cdf4
5
5
  SHA512:
6
- metadata.gz: 2b58d0ed3307893fdd43fcd361b5da63e541baf199ac21860fa11cca756fe4339a63b4bf3420164232c248667e3cf75fe0b467928642e9fcd99f951308fa6e67
7
- data.tar.gz: 3c5cb9a04f22a56a16f51c41faff55a5d587fe51dd2774e6a018846da40b5bf066f08bbbfc7848382ec2a76c64a0b17cad372972b0357d7182359e934864b771
6
+ metadata.gz: 201cad0d07ee914d79af82d8ad2ee10740fbe49002e1f3cbb2740b3d573df61c26532688651e6f6f70ea538e0475f51cbe0a46055b2b6331cd5fff48aa4b8d3e
7
+ data.tar.gz: 74a44c4ac848c9dbe5f0fbd772f2839c5565a8322d7292218d64f7e31f5fdf8e4849e59845fee361d0ca1ed0ecaa2ce067fdc71a86eaf0e50959e0d826742b2f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.12.3)
4
+ rsmp (0.13.2)
5
5
  async (~> 1.29.1)
6
6
  async-io (~> 1.32.2)
7
7
  colorize (~> 0.8.1)
@@ -68,7 +68,7 @@ GEM
68
68
  ffi (1.15.5-x64-mingw32)
69
69
  fiber-local (1.0.0)
70
70
  hana (1.3.7)
71
- json_schemer (0.2.20)
71
+ json_schemer (0.2.21)
72
72
  ecma-re-validator (~> 0.3)
73
73
  hana (~> 1.3)
74
74
  regexp_parser (~> 2.0)
@@ -79,7 +79,7 @@ GEM
79
79
  multi_test (0.1.2)
80
80
  nio4r (2.5.8)
81
81
  rake (13.0.6)
82
- regexp_parser (2.2.1)
82
+ regexp_parser (2.4.0)
83
83
  rsmp_schemer (0.4.0)
84
84
  json_schemer (~> 0.2.18)
85
85
  rspec (3.10.0)
@@ -1,33 +1,94 @@
1
1
  module RSMP
2
2
  # class that tracks the state of an alarm
3
3
  class AlarmState
4
- attr_reader :component_id, :code, :acknowledged, :suspended, :active, :timestamp, :category, :priority
4
+ attr_reader :component_id, :code, :acknowledged, :suspended, :active, :timestamp, :category, :priority, :rvs
5
5
 
6
- def initialize component_id:, code:
7
- @component_id = component_id
6
+ def initialize component:, code:
7
+ @component = component
8
+ @component_id = component.c_id
8
9
  @code = code
10
+ @suspended = false
9
11
  @acknowledged = false
10
12
  @suspended = false
11
13
  @active = false
12
14
  @timestamp = nil
13
- @category = nil
14
- @priority = nil
15
+ @category = 'D'
16
+ @priority = 2
17
+ @rvs = []
15
18
  end
16
19
 
17
20
  def suspend
18
- @suspended = true
21
+ change, @suspended = !@suspended, true
22
+ update_timestamp if change
23
+ change
19
24
  end
20
25
 
21
26
  def resume
22
- @suspended = false
27
+ change, @suspended = @suspended, false
28
+ update_timestamp if change
29
+ change
23
30
  end
24
31
 
25
32
  def activate
26
- @active = true
33
+ change, @active = !@active, true
34
+ update_timestamp if change
35
+ change
27
36
  end
28
37
 
29
38
  def deactivate
30
- @active = false
39
+ change, @active = @active, false
40
+ update_timestamp if change
41
+ change
42
+ end
43
+
44
+ def update_timestamp
45
+ @timestamp = @component.node.clock.now
46
+ end
47
+
48
+ def to_message specialization:
49
+ Alarm.new(
50
+ 'cId' => @component_id,
51
+ 'aSp' => specialization,
52
+ 'aCId' => @code,
53
+ 'aTs' => Clock.to_s(@timestamp),
54
+ 'ack' => (@acknowledged ? 'Acknowledged' : 'notAcknowledged'),
55
+ 'sS' => (@suspended ? 'suspended' : 'notSuspended'),
56
+ 'aS' => (@active ? 'Active' : 'inActive'),
57
+ 'cat' => @category,
58
+ 'pri' => @priority.to_s,
59
+ 'rvs' => @rvs
60
+ )
31
61
  end
62
+
63
+ def differ_from_message? message
64
+ return true if @timestamp != message.attribute('aTs')
65
+ return true if message.attribute('ack') && @acknowledged != (message.attribute('ack') == 'True')
66
+ return true if message.attribute('sS') && @suspended != (message.attribute('sS') == 'True')
67
+ return true if message.attribute('aS') && @active != (message.attribute('aS') == 'True')
68
+ return true if message.attribute('cat') && @category != message.attribute('cat')
69
+ return true if message.attribute('pri') && @priority != message.attribute('pri').to_i
70
+ #return true @rvs = message.attribute('rvs')
71
+ false
72
+ end
73
+
74
+ # update from rsmp message
75
+ # component id, alarm code and specialization are not updated
76
+ def update_from_message message
77
+ unless differ_from_message? message
78
+ raise RepeatedAlarmError.new("no changes from previous alarm #{message.m_id_short}")
79
+ end
80
+ if Time.parse(message.attribute('aTs')) < Time.parse(message.attribute('aTs'))
81
+ raise TimestampError.new("timestamp is earlier than previous alarm #{message.m_id_short}")
82
+ end
83
+ ensure
84
+ @timestamp = message.attribute('aTs')
85
+ @acknowledged = message.attribute('ack') == 'True'
86
+ @suspended = message.attribute('sS') == 'True'
87
+ @active = message.attribute('aS') == 'True'
88
+ @category = message.attribute('cat')
89
+ @priority = message.attribute('pri').to_i
90
+ @rvs = message.attribute('rvs')
91
+ end
92
+
32
93
  end
33
94
  end
@@ -1,9 +1,7 @@
1
1
  module RSMP
2
-
3
2
  # RSMP component
4
-
5
3
  class Component < ComponentBase
6
- def initialize node:, id:, grouped: false
4
+ def initialize node:, id:, ntsOId: nil, xNId: nil, grouped: false
7
5
  super
8
6
  end
9
7
 
@@ -15,40 +13,38 @@ module RSMP
15
13
  raise UnknownStatus.new "Status #{status_code}/#{status_name} not implemented by #{self.class}"
16
14
  end
17
15
 
18
- def get_alarm_state alarm_code
19
- alarm = @alarms[alarm_code] ||= RSMP::AlarmState.new component_id: c_id, code: alarm_code
20
- end
21
-
22
16
  def suspend_alarm alarm_code
23
17
  alarm = get_alarm_state alarm_code
24
- return if alarm.suspended
25
- log "Suspending alarm #{alarm_code}", level: :info
26
- alarm.suspend
27
- @node.alarm_changed alarm
18
+ if alarm.suspend
19
+ log "Suspending alarm #{alarm_code}", level: :info
20
+ @node.alarm_suspended_or_resumed alarm
21
+ else
22
+ log "Alarm #{alarm_code} already suspended", level: :info
23
+ end
28
24
  end
29
25
 
30
26
  def resume_alarm alarm_code
31
27
  alarm = get_alarm_state alarm_code
32
- return unless alarm.suspended
33
- log "Resuming alarm #{alarm_code}", level: :info
34
- alarm.resume
35
- @node.alarm_changed alarm
28
+ if alarm.resume
29
+ log "Resuming alarm #{alarm_code}", level: :info
30
+ @node.alarm_suspended_or_resumed alarm
31
+ else
32
+ log "Alarm #{alarm_code} already resumed", level: :info
33
+ end
36
34
  end
37
35
 
38
36
  def activate_alarm alarm_code
39
37
  alarm = get_alarm_state alarm_code
40
- return if alarm.active
38
+ return unless alarm.activate
41
39
  log "Activating alarm #{alarm_code}", level: :info
42
- alarm.activate
43
- @node.alarm_changed alarm
40
+ @node.alarm_activated_or_deactivated alarm
44
41
  end
45
42
 
46
43
  def deactivate_alarm alarm_code
47
44
  alarm = get_alarm_state alarm_code
48
- return unless alarm.active
45
+ return unless alarm.deactivate
49
46
  log "Deactivating alarm #{alarm_code}", level: :info
50
- alarm.deactivate
51
- @node.alarm_changed alarm
47
+ @node.alarm_activated_or_deactivated alarm
52
48
  end
53
49
  end
54
50
  end
@@ -5,7 +5,8 @@ module RSMP
5
5
  class ComponentBase
6
6
  include Inspect
7
7
 
8
- attr_reader :c_id, :node, :alarms, :statuses, :aggregated_status, :aggregated_status_bools, :grouped
8
+ attr_reader :c_id, :ntsOId, :xNId, :node, :alarms, :statuses,
9
+ :aggregated_status, :aggregated_status_bools, :grouped
9
10
 
10
11
  AGGREGATED_STATUS_KEYS = [ :local_control,
11
12
  :communication_distruption,
@@ -16,12 +17,21 @@ module RSMP
16
17
  :rest,
17
18
  :not_connected ]
18
19
 
19
- def initialize node:, id:, grouped: false
20
+ def initialize node:, id:, ntsOId: nil, xNId: nil, grouped: false
21
+ if grouped==false && (ntsOId || xNId)
22
+ raise RSMP::ConfigurationError.new("ntsOId and xNId are only allowed for grouped objects")
23
+ end
20
24
  @c_id = id
25
+ @ntsOId = ntsOId
26
+ @xNId = xNId
21
27
  @node = node
22
28
  @grouped = grouped
23
- @alarms = {}
24
29
  clear_aggregated_status
30
+ @alarms = {}
31
+ end
32
+
33
+ def get_alarm_state alarm_code
34
+ alarm = @alarms[alarm_code] ||= RSMP::AlarmState.new component: self, code: alarm_code
25
35
  end
26
36
 
27
37
  def clear_aggregated_status
@@ -1,10 +1,9 @@
1
1
  module RSMP
2
-
3
2
  # A proxy to a remote RSMP component.
4
-
5
3
  class ComponentProxy < ComponentBase
6
4
  def initialize node:, id:, grouped: false
7
5
  super
6
+ @alarms = {}
8
7
  @statuses = {}
9
8
  @allow_repeat_updates = {}
10
9
  end
@@ -45,23 +44,12 @@ module RSMP
45
44
  @allow_repeat_updates[sCI].delete(n) if @allow_repeat_updates[sCI]
46
45
  end
47
46
  end
48
-
47
+
49
48
  # handle incoming alarm
50
49
  def handle_alarm message
51
- # code = message.attribute('aCId')
52
- # previous = @alarms[code]
53
- # if previous
54
- # unless message.differ?(previous)
55
- # raise RepeatedAlarmError.new("no changes from previous alarm #{previous.m_id_short}")
56
- # end
57
- # if Time.parse(message.attribute('aTs')) < Time.parse(previous.attribute('aTs'))
58
- # raise TimestampError.new("timestamp is earlier than previous alarm #{previous.m_id_short}")
59
- # end
60
- # end
61
- # p message
62
- # ensure
63
- # @alarms[code] = message
50
+ code = message.attribute('aCId')
51
+ alarm = get_alarm_state code
52
+ alarm.update_from_message message
64
53
  end
65
-
66
- end
54
+ end
67
55
  end
data/lib/rsmp/message.rb CHANGED
@@ -68,7 +68,13 @@ module RSMP
68
68
  when /Acknowledge/i
69
69
  AlarmAcknowledged.new attributes
70
70
  when /Suspend/i
71
- AlarmSuspend.new attributes
71
+ if attributes['sS'] =~ /suspended/i
72
+ AlarmSuspended.new attributes
73
+ elsif attributes['sS'] =~ /notSuspended/i
74
+ AlarmResumed.new attributes
75
+ else
76
+ AlarmSuspend.new attributes
77
+ end
72
78
  when /Resume/i
73
79
  AlarmResume.new attributes
74
80
  else
@@ -264,7 +270,23 @@ module RSMP
264
270
  end
265
271
  end
266
272
 
273
+ class AlarmSuspended < Alarm
274
+ def initialize attributes = {}
275
+ super({
276
+ "aSp" => "Suspend",
277
+ "sS" => "suspended"
278
+ }.merge attributes)
279
+ end
280
+ end
267
281
 
282
+ class AlarmResumed < Alarm
283
+ def initialize attributes = {}
284
+ super({
285
+ "aSp" => "Suspend",
286
+ "sS" => "notSuspended"
287
+ }.merge attributes)
288
+ end
289
+ end
268
290
 
269
291
  class Watchdog < Message
270
292
  def initialize attributes = {}
data/lib/rsmp/proxy.rb CHANGED
@@ -643,5 +643,13 @@ module RSMP
643
643
  return { sent: message }
644
644
  end
645
645
  end
646
+
647
+ def set_nts_message_attributes message
648
+ return unless @site && @site.main
649
+ ntsOId = @site.main.ntsOId
650
+ xNId = @site.main.xNId
651
+ message.attributes['ntsOId'] = ntsOId if ntsOId
652
+ message.attributes['xNId'] = xNId if xNId
653
+ end
646
654
  end
647
655
  end
data/lib/rsmp/site.rb CHANGED
@@ -93,23 +93,31 @@ module RSMP
93
93
  end
94
94
  end
95
95
 
96
- def alarm_state_to_message alarm_state
97
- Alarm.new(
96
+ def alarm_state_to_hash alarm_state
97
+ {
98
98
  'cId' => alarm_state.component_id,
99
- 'aTs' => clock.to_s,
100
99
  'aCId' => alarm_state.code,
101
- 'aSp' => (alarm_state.suspended ? 'Suspend' : 'Issue'),
100
+ 'aTs' => Clock.to_s(alarm_state.timestamp),
102
101
  'ack' => (alarm_state.acknowledged ? 'Acknowledged' : 'notAcknowledged'),
103
102
  'sS' => (alarm_state.suspended ? 'suspended' : 'notSuspended'),
104
103
  'aS' => (alarm_state.active ? 'Active' : 'inActive'),
105
- 'cat' => (alarm_state.category || 'D'),
106
- 'pri' => (alarm_state.priority || '2'),
107
- 'rvs' => []
108
- )
104
+ 'cat' => alarm_state.category,
105
+ 'pri' => alarm_state.priority.to_s,
106
+ 'rvs' => alarm_state.rvs
107
+ }
108
+ end
109
+
110
+ def alarm_suspended_or_resumed alarm_state
111
+ alarm = AlarmIssue.new( alarm_state_to_hash(alarm_state).merge('aSp' => 'Suspend') )
112
+ send_alarm alarm
113
+ end
114
+
115
+ def alarm_activated_or_deactivated alarm_state
116
+ alarm = AlarmIssue.new( alarm_state_to_hash(alarm_state).merge('aSp' => 'Issue') )
117
+ send_alarm alarm
109
118
  end
110
119
 
111
- def alarm_changed alarm_state
112
- alarm = alarm_state_to_message alarm_state
120
+ def send_alarm alarm
113
121
  @proxies.each do |proxy|
114
122
  proxy.send_message alarm if proxy.ready?
115
123
  end
@@ -63,7 +63,7 @@ module RSMP
63
63
  process_aggregated_status message
64
64
  when AggregatedStatusRequest
65
65
  will_not_handle message
66
- when Alarm
66
+ when AlarmIssue, AlarmSuspended, AlarmResumed
67
67
  process_alarm message
68
68
  when CommandResponse
69
69
  process_command_response message
@@ -114,13 +114,11 @@ module RSMP
114
114
  def request_aggregated_status component, options={}
115
115
  validate_ready 'request aggregated status'
116
116
  m_id = options[:m_id] || RSMP::Message.make_m_id
117
-
118
117
  message = RSMP::AggregatedStatusRequest.new({
119
- "ntsOId" => '',
120
- "xNId" => '',
121
- "cId" => component,
122
- "mId" => m_id
118
+ "cId" => component,
119
+ "mId" => m_id
123
120
  })
121
+ set_nts_message_attributes message
124
122
  send_and_optionally_collect message, options do |collect_options|
125
123
  AggregatedStatusCollector.new(
126
124
  self,
@@ -187,12 +185,11 @@ module RSMP
187
185
  request_list = status_list.map { |item| item.slice('sCI','n') }
188
186
 
189
187
  message = RSMP::StatusRequest.new({
190
- "ntsOId" => '',
191
- "xNId" => '',
192
188
  "cId" => component,
193
189
  "sS" => request_list,
194
190
  "mId" => m_id
195
191
  })
192
+ set_nts_message_attributes message
196
193
  send_and_optionally_collect message, options do |collect_options|
197
194
  StatusCollector.new(
198
195
  self,
@@ -232,12 +229,11 @@ module RSMP
232
229
  component.allow_repeat_updates subscribe_list
233
230
 
234
231
  message = RSMP::StatusSubscribe.new({
235
- "ntsOId" => '',
236
- "xNId" => '',
237
232
  "cId" => component_id,
238
233
  "sS" => subscribe_list,
239
234
  'mId' => m_id
240
235
  })
236
+ set_nts_message_attributes message
241
237
  send_and_optionally_collect message, options do |collect_options|
242
238
  StatusCollector.new(
243
239
  self,
@@ -262,11 +258,10 @@ module RSMP
262
258
  end
263
259
 
264
260
  message = RSMP::StatusUnsubscribe.new({
265
- "ntsOId" => '',
266
- "xNId" => '',
267
261
  "cId" => component_id,
268
262
  "sS" => status_list
269
263
  })
264
+ set_nts_message_attributes message
270
265
  send_message message, validate: options[:validate]
271
266
  message
272
267
  end
@@ -292,12 +287,11 @@ module RSMP
292
287
  validate_ready 'send command'
293
288
  m_id = options[:m_id] || RSMP::Message.make_m_id
294
289
  message = RSMP::CommandRequest.new({
295
- "ntsOId" => '',
296
- "xNId" => '',
297
290
  "cId" => component,
298
291
  "arg" => command_list,
299
292
  "mId" => m_id
300
293
  })
294
+ set_nts_message_attributes message
301
295
  send_and_optionally_collect message, options do |collect_options|
302
296
  CommandResponseCollector.new(
303
297
  self,
@@ -105,7 +105,7 @@ module RSMP
105
105
  process_status_subcribe message
106
106
  when StatusUnsubscribe
107
107
  process_status_unsubcribe message
108
- when AlarmAcknowledged, AlarmSuspend, AlarmResume, AlarmRequest
108
+ when Alarm, AlarmAcknowledged, AlarmSuspend, AlarmResume, AlarmRequest
109
109
  process_alarm message
110
110
  else
111
111
  super message
@@ -160,7 +160,7 @@ module RSMP
160
160
  "se" => component.aggregated_status_bools,
161
161
  "mId" => m_id,
162
162
  })
163
-
163
+ set_nts_message_attributes message
164
164
  send_and_optionally_collect message, options do |collect_options|
165
165
  Collector.new self, collect_options.merge(task:@task, type: 'MessageAck')
166
166
  end
@@ -9,9 +9,9 @@ module RSMP
9
9
  :functional_position,
10
10
  :startup_sequence_active, :startup_sequence, :startup_sequence_pos
11
11
 
12
- def initialize node:, id:, cycle_time: 10, signal_plans:,
12
+ def initialize node:, id:, ntsOId: nil, xNId: nil, cycle_time: 10, signal_plans:,
13
13
  startup_sequence:, live_output:nil, inputs:{}
14
- super node: node, id: id, grouped: true
14
+ super node: node, id: id, ntsOId: ntsOId, xNId: xNId, grouped: true
15
15
  @signal_groups = []
16
16
  @detector_logics = []
17
17
  @plans = signal_plans
@@ -52,7 +52,10 @@ module RSMP
52
52
  def build_component id:, type:, settings:{}
53
53
  component = case type
54
54
  when 'main'
55
- @main = TrafficController.new node: self, id: id,
55
+ @main = TrafficController.new node: self,
56
+ id: id,
57
+ ntsOId: settings['ntsOId'],
58
+ xNId: settings['xNId'],
56
59
  cycle_time: settings['cycle_time'],
57
60
  startup_sequence: @startup_sequence,
58
61
  signal_plans: @signal_plans,
data/lib/rsmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = "0.12.3"
2
+ VERSION = "0.13.2"
3
3
  end
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.12.3
4
+ version: 0.13.2
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-04-05 00:00:00.000000000 Z
11
+ date: 2022-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async