rsmp 0.11.7 → 0.12.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: 85f5c9ffaa15773f1f5e6722bd93fcf8ecff524166859bafec85e6871a285330
4
- data.tar.gz: 90f8edb63fb20d324885c9e9b15c5bde49bcbdc80086fa48c36fd4c78b183475
3
+ metadata.gz: 957caaa3191011a85a0284ef530434184e15620b7762e4971a8547f45a955ec3
4
+ data.tar.gz: 8905eb13b36d26d6c50fe0e4466f40e2f742d18384e93bc2b7e67d2e77975441
5
5
  SHA512:
6
- metadata.gz: 01c22ef28ddbaedcf45d7c24518d1cf9ffe190f434c0e8e0e07a9e7d12f6333ea5ccc16f2532502755d1efa6619e9a2c7d73e462aeed9ea75f93deb55e4cb0ab
7
- data.tar.gz: ebca1461398deacd9ff211e0bbc24c4586e286d46806c8b62718a36eaf41b5b53c2cef5a6f1882b81d4009778172a0173bf390812478e47bca86927465bde179
6
+ metadata.gz: 100b1c357a27939ec7142ac0f63e9589ee36a044bfc54fcaea30303c2fb1eba277a3d0e1927f578fabe99717f9ffeaa83f5eceeb65dd7492d260458ab4d61689
7
+ data.tar.gz: 729b6355adef1914f9621c10c69bd88318c688b40ef2881125a0783ed83533a4f34bc1e1192e338d073f44af4e7ae6c416732693622fa7ef478a48b63c31ba7d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.11.7)
4
+ rsmp (0.12.0)
5
5
  async (~> 1.29.1)
6
6
  async-io (~> 1.32.2)
7
7
  colorize (~> 0.8.1)
@@ -27,7 +27,7 @@ GEM
27
27
  builder (3.2.4)
28
28
  childprocess (4.1.0)
29
29
  colorize (0.8.1)
30
- console (1.14.0)
30
+ console (1.15.0)
31
31
  fiber-local
32
32
  contracts (0.17)
33
33
  cucumber (7.1.0)
data/config/tlc.yaml CHANGED
@@ -51,8 +51,11 @@ security_codes:
51
51
  log:
52
52
  ip: false
53
53
  site_id: 9
54
- component: 2
54
+ component: 3
55
55
  level: false
56
56
  debug: true
57
57
  json: true
58
58
  live_output: tmp/tlc.state
59
+ skip_validation:
60
+ - AlarmSuspend
61
+ - AlarmResume
@@ -0,0 +1,25 @@
1
+ module RSMP
2
+ # class that tracks the state of an alarm
3
+ class AlarmState
4
+ attr_reader :component_id, :code, :acknowledged, :suspended, :active, :timestamp, :category, :priority
5
+
6
+ def initialize component_id:, code:
7
+ @component_id = component_id
8
+ @code = code
9
+ @acknowledged = false
10
+ @suspended = false
11
+ @active = false
12
+ @timestamp = nil
13
+ @category = nil
14
+ @priority = nil
15
+ end
16
+
17
+ def suspend
18
+ @suspended = true
19
+ end
20
+
21
+ def resume
22
+ @suspended = false
23
+ end
24
+ end
25
+ end
@@ -159,6 +159,7 @@ module RSMP
159
159
  if message.attribute('oMId') == @options[:m_id]
160
160
  m_id_short = RSMP::Message.shorten_m_id @options[:m_id], 8
161
161
  cancel RSMP::MessageRejected.new("#{@title} #{m_id_short} was rejected with '#{message.attribute('rea')}'")
162
+ @notifier.log "#{identifier}: cancelled due to a NotAck", level: :debug
162
163
  true
163
164
  end
164
165
  end
@@ -1,72 +1,10 @@
1
1
  module RSMP
2
2
 
3
- # Class that represents an RMSP component.
4
- # Currently this class is used by both SiteProxy and SupervisorProxy, and can
5
- # therefore represent either a local or remote (proxy) component.
6
-
7
- class Component
8
- include Inspect
9
-
10
- attr_reader :c_id, :node, :alarms, :statuses, :aggregated_status, :aggregated_status_bools, :grouped
11
-
12
- AGGREGATED_STATUS_KEYS = [ :local_control,
13
- :communication_distruption,
14
- :high_priority_alarm,
15
- :medium_priority_alarm,
16
- :low_priority_alarm,
17
- :normal,
18
- :rest,
19
- :not_connected ]
3
+ # RSMP component
20
4
 
5
+ class Component < ComponentBase
21
6
  def initialize node:, id:, grouped: false
22
- @c_id = id
23
- @node = node
24
- @grouped = grouped
25
- @alarms = {}
26
- @statuses = {}
27
- @subscribes = {}
28
- clear_aggregated_status
29
- end
30
-
31
- def clear_aggregated_status
32
- @aggregated_status = []
33
- @aggregated_status_bools = Array.new(8,false)
34
- @aggregated_status_bools[5] = true
35
- end
36
-
37
- def set_aggregated_status status, options={}
38
- status = [status] if status.is_a? Symbol
39
- raise InvalidArgument unless status.is_a? Array
40
- input = status & AGGREGATED_STATUS_KEYS
41
- if input != @aggregated_status
42
- AGGREGATED_STATUS_KEYS.each_with_index do |key,index|
43
- @aggregated_status_bools[index] = status.include?(key)
44
- end
45
- aggregated_status_changed options
46
- end
47
- end
48
-
49
- def set_aggregated_status_bools status
50
- raise InvalidArgument unless status.is_a? Array
51
- raise InvalidArgument unless status.size == 8
52
- if status != @aggregated_status_bools
53
- @aggregated_status = []
54
- AGGREGATED_STATUS_KEYS.each_with_index do |key,index|
55
- on = status[index] == true
56
- @aggregated_status_bools[index] = on
57
- @aggregated_status << key if on
58
- end
59
- aggregated_status_changed
60
- end
61
- end
62
-
63
- def aggregated_status_changed options={}
64
- @node.aggregated_status_changed self, options
65
- end
66
-
67
- def log str, options
68
- default = { component: c_id}
69
- @node.log str, default.merge(options)
7
+ super
70
8
  end
71
9
 
72
10
  def handle_command command_code, arg
@@ -77,108 +15,35 @@ module RSMP
77
15
  raise UnknownStatus.new "Status #{status_code}/#{status_name} not implemented by #{self.class}"
78
16
  end
79
17
 
80
- # handle incoming alarm
81
- def handle_alarm message
82
- code = message.attribute('aCId')
83
- previous = @alarms[code]
84
- if previous
85
- unless message.differ?(previous)
86
- raise RepeatedAlarmError.new("no changes from previous alarm #{previous.m_id_short}")
87
- end
88
- if Time.parse(message.attribute('aTs')) < Time.parse(previous.attribute('aTs'))
89
- raise TimestampError.new("timestamp is earlier than previous alarm #{previous.m_id_short}")
90
- end
91
- end
92
- ensure
93
- @alarms[code] = message
94
- end
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
-
117
- # Handle an incoming status respone, by storing the values
118
- def handle_status_response message
119
- store_status message, check_repeated: false
18
+ def get_alarm_state alarm_code
19
+ alarm = @alarms[alarm_code] ||= RSMP::AlarmState.new component_id: c_id, code: alarm_code
120
20
  end
121
21
 
122
- # Handle an incoming status update, by storing the values
123
- def handle_status_update message
124
- store_status message, check_repeated: true
125
- end
126
-
127
- # Our proxy subscribed to status updates
128
- # Store update rates, so we can check for repeated alarm if we asked for updates only
129
- # when there's a change, not on a regular interval.
130
- # After subscribing, an update status us send regarless of whether values changes,
131
- # and we store that.
132
- def handle_status_subscribe status_list
133
- status_list.each do |item|
134
- sCI, n, uRt = item['sCI'], item['n'], item['uRt']
135
-
136
- # record the update rate, so we can check for repeated status values if rate is zero
137
- @subscribes[sCI] ||= {}
138
- @subscribes[sCI][n] = {'uRt'=>uRt}
139
-
140
- # record that we expect an upeate, even though the value might not change
141
- @statuses[sCI] ||= {}
142
- @statuses[sCI][n] ||= {}
143
- @statuses[sCI][n][:initial] = true
22
+ def suspend_alarm alarm_code
23
+ alarm_state = get_alarm_state alarm_code
24
+ if alarm.suspended == false
25
+ log "Suspending alarm #{alarm_code}", level: :info
26
+ alarm.suspend
27
+ @node.alarm_suspended_or_resumed alarm
28
+ else
29
+ log "Alarm #{alarm_code} already suspended", level: :info
144
30
  end
145
31
  end
146
32
 
147
- # Our proxy unsubscribed to status updates.
148
- # Update our list of update rates.
149
- def handle_status_unsubscribe status_list
150
- status_list.each do |item|
151
- sCI, n = item['sCI'], item['n']
152
- if @subscribes[sCI]
153
- @subscribes[sCI].delete n
154
- end
155
- if @subscribes[sCI].empty?
156
- @subscribes.delete sCI
157
- end
158
-
159
- # remove any mark that would allow the next update to be a repeat
160
- item = @statuses.dig sCI, n
161
- item.delete(:initial) if item
33
+ def resume_alarm alarm_code
34
+ alarm_state = get_alarm_state alarm_code
35
+ if alarm.suspended
36
+ log "Resuming alarm #{alarm_code}", level: :info
37
+ alarm.resume
38
+ @node.alarm_suspended_or_resumed alarm
39
+ else
40
+ log "Alarm #{alarm_code} not suspended", level: :info
162
41
  end
163
42
  end
164
43
 
165
- # Store the latest status update values, optionally
166
- # checking that we're not receiving unchanged values if we're subscribed
167
- # wit updates only on change
168
- def store_status message, check_repeated:
169
- message.attribute('sS').each do |item|
170
- sCI, n, s, q = item['sCI'], item['n'], item['s'], item['q']
171
- uRt = @subscribes.dig(sCI,n,'uRt')
172
- new_values = {'s'=>s,'q'=>q}
173
- old_values = @statuses.dig(sCI,n)
174
- if check_repeated && uRt.to_i == 0
175
- if new_values == old_values
176
- raise RSMP::RepeatedStatusError.new "no change for #{sCI} '#{n}'"
177
- end
178
- end
179
- @statuses[sCI] ||= {}
180
- @statuses[sCI][n] = new_values
181
- end
44
+ # send alarm
45
+ def send_alarm code:, status:
46
+ @node.alarm_changed self, alarm
182
47
  end
183
48
 
184
49
  end
@@ -0,0 +1,69 @@
1
+ module RSMP
2
+
3
+ # RSMP component base class.
4
+
5
+ class ComponentBase
6
+ include Inspect
7
+
8
+ attr_reader :c_id, :node, :alarms, :statuses, :aggregated_status, :aggregated_status_bools, :grouped
9
+
10
+ AGGREGATED_STATUS_KEYS = [ :local_control,
11
+ :communication_distruption,
12
+ :high_priority_alarm,
13
+ :medium_priority_alarm,
14
+ :low_priority_alarm,
15
+ :normal,
16
+ :rest,
17
+ :not_connected ]
18
+
19
+ def initialize node:, id:, grouped: false
20
+ @c_id = id
21
+ @node = node
22
+ @grouped = grouped
23
+ @alarms = {}
24
+ clear_aggregated_status
25
+ end
26
+
27
+ def clear_aggregated_status
28
+ @aggregated_status = []
29
+ @aggregated_status_bools = Array.new(8,false)
30
+ @aggregated_status_bools[5] = true
31
+ end
32
+
33
+ def log str, options
34
+ default = { component: c_id}
35
+ @node.log str, default.merge(options)
36
+ end
37
+
38
+ def set_aggregated_status status, options={}
39
+ status = [status] if status.is_a? Symbol
40
+ raise InvalidArgument unless status.is_a? Array
41
+ input = status & AGGREGATED_STATUS_KEYS
42
+ if input != @aggregated_status
43
+ AGGREGATED_STATUS_KEYS.each_with_index do |key,index|
44
+ @aggregated_status_bools[index] = status.include?(key)
45
+ end
46
+ aggregated_status_changed options
47
+ end
48
+ end
49
+
50
+ def set_aggregated_status_bools status
51
+ raise InvalidArgument unless status.is_a? Array
52
+ raise InvalidArgument unless status.size == 8
53
+ if status != @aggregated_status_bools
54
+ @aggregated_status = []
55
+ AGGREGATED_STATUS_KEYS.each_with_index do |key,index|
56
+ on = status[index] == true
57
+ @aggregated_status_bools[index] = on
58
+ @aggregated_status << key if on
59
+ end
60
+ aggregated_status_changed
61
+ end
62
+ end
63
+
64
+ def aggregated_status_changed options={}
65
+ @node.aggregated_status_changed self, options
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,67 @@
1
+ module RSMP
2
+
3
+ # A proxy to a remote RSMP component.
4
+
5
+ class ComponentProxy < ComponentBase
6
+ def initialize node:, id:, grouped: false
7
+ super
8
+ @statuses = {}
9
+ @allow_repeat_updates = {}
10
+ end
11
+
12
+ # allow the next status update to be a repeat value
13
+ def allow_repeat_updates subscribe_list
14
+ subscribe_list.each do |item|
15
+ sCI = item['sCI']
16
+ n = item['n']
17
+ @allow_repeat_updates[sCI] ||= Set.new # Set is like an array, but with no duplicates
18
+ @allow_repeat_updates[sCI] << n
19
+ end
20
+ end
21
+
22
+ # Check that were not receiving repeated update values.
23
+ # The check is not performed for item with an update interval.
24
+ def check_repeat_values message, subscription_list
25
+ message.attribute('sS').each do |item|
26
+ sCI, n, s, q = item['sCI'], item['n'], item['s'], item['q']
27
+ uRt = subscription_list.dig(c_id,sCI,n,'uRt')
28
+ next if uRt.to_i > 0
29
+ next if @allow_repeat_updates[sCI] && @allow_repeat_updates[sCI].include?(n)
30
+ new_values = {'s'=>s,'q'=>q}
31
+ old_values = @statuses.dig(sCI,n)
32
+ if new_values == old_values
33
+ raise RSMP::RepeatedStatusError.new "no change for #{sCI} '#{n}'"
34
+ end
35
+ end
36
+ end
37
+ # Store the latest status update values
38
+ def store_status message
39
+ message.attribute('sS').each do |item|
40
+ sCI, n, s, q = item['sCI'], item['n'], item['s'], item['q']
41
+ @statuses[sCI] ||= {}
42
+ @statuses[sCI][n] = {'s'=>s,'q'=>q}
43
+
44
+ # once a value is received, don't allow the value to be a repeat
45
+ @allow_repeat_updates[sCI].delete(n) if @allow_repeat_updates[sCI]
46
+ end
47
+ end
48
+
49
+ # handle incoming alarm
50
+ 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
64
+ end
65
+
66
+ end
67
+ end
@@ -36,9 +36,9 @@ module RSMP
36
36
  @components[component.c_id] = component
37
37
  end
38
38
 
39
- def build_component id:, type:, settings:{}
40
- Component.new id:id, node: self, grouped: type=='main'
41
- end
39
+ #def build_component id:, type:, settings:{}
40
+ # Component.new id:id, node: self, grouped: type=='main'
41
+ #end
42
42
 
43
43
  def infer_component_type component_id
44
44
  Component
data/lib/rsmp/message.rb CHANGED
@@ -61,15 +61,15 @@ module RSMP
61
61
 
62
62
  def self.build_alarm attributes
63
63
  case attributes["aSp"]
64
- when 'Issue'
64
+ when /Issue/i
65
65
  AlarmIssue.new attributes
66
- when 'Request'
66
+ when /Request/i
67
67
  AlarmRequest.new attributes
68
- when 'Acknowledge'
68
+ when /Acknowledge/i
69
69
  AlarmAcknowledged.new attributes
70
- when 'Suspend'
70
+ when /Suspend/i
71
71
  AlarmSuspend.new attributes
72
- when 'Resume'
72
+ when /Resume/i
73
73
  AlarmResume.new attributes
74
74
  else
75
75
  Alarm.new attributes
data/lib/rsmp/proxy.rb CHANGED
@@ -438,6 +438,7 @@ module RSMP
438
438
  end
439
439
 
440
440
  def will_not_handle message
441
+ "WILL NOT HANDLE"
441
442
  reason = "since we're a #{self.class.name.downcase}" unless reason
442
443
  log "Ignoring #{message.type}, #{reason}", message: message, level: :warning
443
444
  dont_acknowledge message, nil, reason
data/lib/rsmp/site.rb CHANGED
@@ -93,9 +93,25 @@ module RSMP
93
93
  end
94
94
  end
95
95
 
96
- def alarm_changed component, alarm
96
+ def alarm_state_to_message alarm_state
97
+ Alarm.new(
98
+ 'cId' => alarm_state.component_id,
99
+ 'aTs' => clock.to_s,
100
+ 'aCId' => alarm_state.code,
101
+ 'aSp' => (alarm_state.suspended ? 'Suspend' : 'Issue'),
102
+ 'ack' => (alarm_state.acknowledged ? 'Acknowledged' : 'notAcknowledged'),
103
+ 'sS' => (alarm_state.suspended ? 'suspended' : 'notSuspended'),
104
+ 'aS' => (alarm_state.active ? 'Active' : 'inActive'),
105
+ 'cat' => (alarm_state.category || 'D'),
106
+ 'pri' => (alarm_state.priority || '2'),
107
+ 'rvs' => []
108
+ )
109
+ end
110
+
111
+ def alarm_suspended_or_resumed alarm_state
112
+ alarm = alarm_state_to_message alarm_state
97
113
  @proxies.each do |proxy|
98
- proxy.send_alarm component, alarm if proxy.ready?
114
+ proxy.send_message alarm if proxy.ready?
99
115
  end
100
116
  end
101
117
 
@@ -135,5 +151,9 @@ module RSMP
135
151
  end
136
152
  nil
137
153
  end
154
+
155
+ def build_component id:, type:, settings:{}
156
+ Component.new id:id, node: self, grouped: type=='main'
157
+ end
138
158
  end
139
159
  end
@@ -12,6 +12,7 @@ module RSMP
12
12
  @supervisor = options[:supervisor]
13
13
  @settings = @supervisor.supervisor_settings.clone
14
14
  @site_id = options[:site_id]
15
+ @status_subscriptions = {}
15
16
  end
16
17
 
17
18
  # handle communication
@@ -203,7 +204,7 @@ module RSMP
203
204
 
204
205
  def process_status_response message
205
206
  component = find_component message.attribute("cId")
206
- component.handle_status_response message
207
+ component.store_status message
207
208
  log "Received #{message.type}", message: message, level: :log
208
209
  acknowledge message
209
210
  end
@@ -213,11 +214,23 @@ module RSMP
213
214
  m_id = options[:m_id] || RSMP::Message.make_m_id
214
215
 
215
216
  # additional items can be used when verifying the response,
216
- # but must to remove from the subscribe message
217
+ # but must be removed from the subscribe message
217
218
  subscribe_list = status_list.map { |item| item.slice('sCI','n','uRt') }
218
219
 
220
+ # update our subcription list
221
+ @status_subscriptions[component_id] ||= {}
222
+ subscribe_list.each do |item|
223
+ sCI = item["sCI"]
224
+ n = item["n"]
225
+ uRt = item["uRt"]
226
+ @status_subscriptions[component_id][sCI] ||= {}
227
+ @status_subscriptions[component_id][sCI][n] ||= {}
228
+ @status_subscriptions[component_id][sCI][n]['uRt'] = uRt
229
+ end
230
+
231
+ p @status_subscriptions
219
232
  component = find_component component_id
220
- component.handle_status_subscribe subscribe_list
233
+ component.allow_repeat_updates subscribe_list
221
234
 
222
235
  message = RSMP::StatusSubscribe.new({
223
236
  "ntsOId" => '',
@@ -236,15 +249,24 @@ module RSMP
236
249
  end
237
250
 
238
251
  def unsubscribe_to_status component_id, status_list, options={}
239
- component = find_component component_id
240
- component.handle_status_subscribe status_list
241
-
242
252
  validate_ready 'unsubscribe to status'
253
+
254
+ # update our subcription list
255
+ status_list.each do |item|
256
+ sCI = item["sCI"]
257
+ n = item["n"]
258
+ if @status_subscriptions.dig(component_id,sCI,n)
259
+ @status_subscriptions[component_id][sCI].delete n
260
+ @status_subscriptions[component_id].delete(sCI) if @status_subscriptions[component_id][sCI].empty?
261
+ @status_subscriptions.delete(component_id) if @status_subscriptions[component_id].empty?
262
+ end
263
+ end
264
+
243
265
  message = RSMP::StatusUnsubscribe.new({
244
- "ntsOId" => '',
245
- "xNId" => '',
246
- "cId" => component_id,
247
- "sS" => status_list
266
+ "ntsOId" => '',
267
+ "xNId" => '',
268
+ "cId" => component_id,
269
+ "sS" => status_list
248
270
  })
249
271
  send_message message, validate: options[:validate]
250
272
  message
@@ -252,7 +274,8 @@ module RSMP
252
274
 
253
275
  def process_status_update message
254
276
  component = find_component message.attribute("cId")
255
- component.handle_status_update message
277
+ component.check_repeat_values message, @status_subscriptions
278
+ component.store_status message
256
279
  log "Received #{message.type}", message: message, level: :log
257
280
  acknowledge message
258
281
  end
@@ -357,5 +380,10 @@ module RSMP
357
380
  @supervisor.notify_error e, options if @supervisor
358
381
  distribute_error e, options
359
382
  end
383
+
384
+ def build_component id:, type:, settings:{}
385
+ ComponentProxy.new id:id, node: self, grouped: type=='main'
386
+ end
387
+
360
388
  end
361
389
  end
@@ -91,25 +91,24 @@ module RSMP
91
91
 
92
92
  def process_message message
93
93
  case message
94
- when Alarm
95
- when StatusResponse
96
- when StatusUpdate
97
- when AggregatedStatus
98
- will_not_handle message
99
- when AggregatedStatusRequest
100
- process_aggregated_status_request message
101
- when CommandRequest
102
- process_command_request message
103
- when CommandResponse
104
- process_command_response message
105
- when StatusRequest
106
- process_status_request message
107
- when StatusSubscribe
108
- process_status_subcribe message
109
- when StatusUnsubscribe
110
- process_status_unsubcribe message
111
- else
112
- super message
94
+ when StatusResponse, StatusUpdate, AggregatedStatus, AlarmIssue
95
+ will_not_handle message
96
+ when AggregatedStatusRequest
97
+ process_aggregated_status_request message
98
+ when CommandRequest
99
+ process_command_request message
100
+ when CommandResponse
101
+ process_command_response message
102
+ when StatusRequest
103
+ process_status_request message
104
+ when StatusSubscribe
105
+ process_status_subcribe message
106
+ when StatusUnsubscribe
107
+ process_status_unsubcribe message
108
+ when AlarmAcknowledged, AlarmSuspend, AlarmResume, AlarmRequest
109
+ process_alarm message
110
+ else
111
+ super message
113
112
  end
114
113
  rescue UnknownComponent, UnknownCommand, UnknownStatus,
115
114
  MessageRejected, MissingAttribute => e
@@ -182,13 +181,39 @@ module RSMP
182
181
  end
183
182
 
184
183
  def process_alarm message
184
+ case message
185
+ when AlarmAcknowledged
186
+ handle_alarm_acknowledge message
187
+ when AlarmSuspend
188
+ handle_alarm_suspend message
189
+ when AlarmResume
190
+ handle_alarm_resume message
191
+ when AlarmRequest
192
+ handle_alarm_request message
193
+ else
194
+ dont_acknowledge message, "Invalid alarm message type"
195
+ end
196
+ end
197
+
198
+ # handle incoming alarm suspend
199
+ def handle_alarm_suspend message
200
+ component_id = message.attributes["cId"]
201
+ component = @site.find_component component_id
185
202
  alarm_code = message.attribute("aCId")
186
- asp = message.attribute("aSp")
187
- status = ["ack","aS","sS"].map { |key| message.attribute(key) }.join(',')
188
- log "Received #{message.type}, #{alarm_code} #{asp} [#{status}]", message: message, level: :log
203
+ log "Received #{message.type} #{alarm_code} suspend", message: message, level: :log
189
204
  acknowledge message
205
+ component.suspend_alarm alarm_code
190
206
  end
191
207
 
208
+ # handle incoming alarm resume
209
+ def handle_alarm_resume message
210
+ component_id = message.attributes["cId"]
211
+ component = @site.find_component component_id
212
+ alarm_code = message.attribute("aCId")
213
+ log "Received #{message.type} #{alarm_code} resume", message: message, level: :log
214
+ acknowledge message
215
+ component.resume_alarm alarm_code
216
+ end
192
217
  # reorganize rmsp command request arg attribute:
193
218
  # [{"cCI":"M0002","cO":"setPlan","n":"status","v":"True"},{"cCI":"M0002","cO":"setPlan","n":"securityCode","v":"5678"},{"cCI":"M0002","cO":"setPlan","n":"timeplan","v":"3"}]
194
219
  # into the simpler, but equivalent:
@@ -262,24 +287,28 @@ module RSMP
262
287
  # for each component, containing all the requested statuses
263
288
 
264
289
  update_list = {}
265
- component = message.attributes["cId"]
266
- @status_subscriptions[component] ||= {}
267
- update_list[component] ||= {}
290
+ component_id = message.attributes["cId"]
291
+ @status_subscriptions[component_id] ||= {}
292
+ update_list[component_id] ||= {}
268
293
  now = Time.now # internal timestamp
269
- subs = @status_subscriptions[component]
294
+ subs = @status_subscriptions[component_id]
270
295
 
271
296
  message.attributes["sS"].each do |arg|
272
297
  sCI = arg["sCI"]
273
298
  subcription = {interval: arg["uRt"].to_i, last_sent_at: now}
274
299
  subs[sCI] ||= {}
275
300
  subs[sCI][arg["n"]] = subcription
276
- update_list[component][sCI] ||= []
277
- update_list[component][sCI] << arg["n"]
301
+ update_list[component_id][sCI] ||= []
302
+ update_list[component_id][sCI] << arg["n"]
278
303
  end
279
304
  acknowledge message
280
305
  send_status_updates update_list # send status after subscribing is accepted
281
306
  end
282
307
 
308
+ def get_status_subscribe_interval component_id, sCI, n
309
+ @status_subscriptions.dig component_id, sCI, n
310
+ end
311
+
283
312
  def process_status_unsubcribe message
284
313
  log "Received #{message.type}", message: message, level: :log
285
314
  component = message.attributes["cId"]
data/lib/rsmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = "0.11.7"
2
+ VERSION = "0.12.0"
3
3
  end
data/lib/rsmp.rb CHANGED
@@ -30,7 +30,10 @@ require 'rsmp/collect/status_collector'
30
30
  require 'rsmp/collect/command_response_collector'
31
31
  require 'rsmp/collect/aggregated_status_collector'
32
32
  require 'rsmp/collect/alarm_collector'
33
+ require 'rsmp/alarm_state'
34
+ require 'rsmp/component_base'
33
35
  require 'rsmp/component'
36
+ require 'rsmp/component_proxy'
34
37
  require 'rsmp/site'
35
38
  require 'rsmp/proxy'
36
39
  require 'rsmp/supervisor_proxy'
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.11.7
4
+ version: 0.12.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-25 00:00:00.000000000 Z
11
+ date: 2022-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -208,6 +208,7 @@ files:
208
208
  - documentation/tasks.md
209
209
  - exe/rsmp
210
210
  - lib/rsmp.rb
211
+ - lib/rsmp/alarm_state.rb
211
212
  - lib/rsmp/archive.rb
212
213
  - lib/rsmp/cli.rb
213
214
  - lib/rsmp/collect/aggregated_status_collector.rb
@@ -225,6 +226,8 @@ files:
225
226
  - lib/rsmp/collect/status_collector.rb
226
227
  - lib/rsmp/collect/status_query.rb
227
228
  - lib/rsmp/component.rb
229
+ - lib/rsmp/component_base.rb
230
+ - lib/rsmp/component_proxy.rb
228
231
  - lib/rsmp/components.rb
229
232
  - lib/rsmp/convert/export/json_schema.rb
230
233
  - lib/rsmp/convert/import/yaml.rb