rsmp 0.35.2 → 0.38.0

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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/devcontainer.json +22 -0
  3. data/.github/copilot-instructions.md +33 -0
  4. data/.github/workflows/copilot-setup-steps.yml +35 -0
  5. data/.github/workflows/rspec.yaml +2 -1
  6. data/.github/workflows/rubocop.yaml +17 -0
  7. data/.gitignore +7 -7
  8. data/.rubocop.yml +80 -0
  9. data/Gemfile +13 -1
  10. data/Gemfile.lock +73 -35
  11. data/Rakefile +3 -3
  12. data/bin/console +2 -4
  13. data/documentation/tasks.md +4 -4
  14. data/lib/rsmp/cli.rb +147 -124
  15. data/lib/rsmp/collect/ack_collector.rb +8 -7
  16. data/lib/rsmp/collect/aggregated_status_collector.rb +4 -4
  17. data/lib/rsmp/collect/alarm_collector.rb +31 -23
  18. data/lib/rsmp/collect/alarm_matcher.rb +3 -3
  19. data/lib/rsmp/collect/collector/logging.rb +17 -0
  20. data/lib/rsmp/collect/collector/reporting.rb +44 -0
  21. data/lib/rsmp/collect/collector/status.rb +34 -0
  22. data/lib/rsmp/collect/collector.rb +69 -150
  23. data/lib/rsmp/collect/command_matcher.rb +19 -6
  24. data/lib/rsmp/collect/command_response_collector.rb +7 -7
  25. data/lib/rsmp/collect/distributor.rb +14 -11
  26. data/lib/rsmp/collect/filter.rb +31 -15
  27. data/lib/rsmp/collect/matcher.rb +7 -11
  28. data/lib/rsmp/collect/queue.rb +4 -4
  29. data/lib/rsmp/collect/receiver.rb +10 -12
  30. data/lib/rsmp/collect/state_collector.rb +116 -77
  31. data/lib/rsmp/collect/status_collector.rb +6 -6
  32. data/lib/rsmp/collect/status_matcher.rb +17 -7
  33. data/lib/rsmp/{alarm_state.rb → component/alarm_state.rb} +76 -37
  34. data/lib/rsmp/{component.rb → component/component.rb} +15 -15
  35. data/lib/rsmp/component/component_base.rb +89 -0
  36. data/lib/rsmp/component/component_proxy.rb +75 -0
  37. data/lib/rsmp/component/components.rb +63 -0
  38. data/lib/rsmp/convert/export/json_schema.rb +116 -110
  39. data/lib/rsmp/convert/import/yaml.rb +21 -18
  40. data/lib/rsmp/{rsmp.rb → helpers/clock.rb} +5 -6
  41. data/lib/rsmp/{deep_merge.rb → helpers/deep_merge.rb} +2 -1
  42. data/lib/rsmp/helpers/error.rb +71 -0
  43. data/lib/rsmp/{inspect.rb → helpers/inspect.rb} +6 -10
  44. data/lib/rsmp/log/archive.rb +98 -0
  45. data/lib/rsmp/log/colorization.rb +41 -0
  46. data/lib/rsmp/log/filtering.rb +54 -0
  47. data/lib/rsmp/log/logger.rb +206 -0
  48. data/lib/rsmp/{logging.rb → log/logging.rb} +5 -7
  49. data/lib/rsmp/message.rb +159 -148
  50. data/lib/rsmp/{node.rb → node/node.rb} +19 -17
  51. data/lib/rsmp/node/protocol.rb +37 -0
  52. data/lib/rsmp/node/site/site.rb +195 -0
  53. data/lib/rsmp/node/supervisor/modules/configuration.rb +59 -0
  54. data/lib/rsmp/node/supervisor/modules/connection.rb +140 -0
  55. data/lib/rsmp/node/supervisor/modules/sites.rb +64 -0
  56. data/lib/rsmp/node/supervisor/supervisor.rb +72 -0
  57. data/lib/rsmp/{task.rb → node/task.rb} +29 -19
  58. data/lib/rsmp/proxy/modules/acknowledgements.rb +144 -0
  59. data/lib/rsmp/proxy/modules/receive.rb +119 -0
  60. data/lib/rsmp/proxy/modules/send.rb +76 -0
  61. data/lib/rsmp/proxy/modules/state.rb +25 -0
  62. data/lib/rsmp/proxy/modules/tasks.rb +105 -0
  63. data/lib/rsmp/proxy/modules/versions.rb +69 -0
  64. data/lib/rsmp/proxy/modules/watchdogs.rb +66 -0
  65. data/lib/rsmp/proxy/proxy.rb +199 -0
  66. data/lib/rsmp/proxy/site/modules/aggregated_status.rb +52 -0
  67. data/lib/rsmp/proxy/site/modules/alarms.rb +27 -0
  68. data/lib/rsmp/proxy/site/modules/commands.rb +31 -0
  69. data/lib/rsmp/proxy/site/modules/status.rb +110 -0
  70. data/lib/rsmp/proxy/site/site_proxy.rb +205 -0
  71. data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +47 -0
  72. data/lib/rsmp/proxy/supervisor/modules/alarms.rb +73 -0
  73. data/lib/rsmp/proxy/supervisor/modules/commands.rb +53 -0
  74. data/lib/rsmp/proxy/supervisor/modules/status.rb +204 -0
  75. data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +178 -0
  76. data/lib/rsmp/tlc/detector_logic.rb +18 -34
  77. data/lib/rsmp/tlc/input_states.rb +126 -0
  78. data/lib/rsmp/tlc/modules/detector_logics.rb +50 -0
  79. data/lib/rsmp/tlc/modules/display.rb +78 -0
  80. data/lib/rsmp/tlc/modules/helpers.rb +41 -0
  81. data/lib/rsmp/tlc/modules/inputs.rb +173 -0
  82. data/lib/rsmp/tlc/modules/modes.rb +253 -0
  83. data/lib/rsmp/tlc/modules/outputs.rb +30 -0
  84. data/lib/rsmp/tlc/modules/plans.rb +218 -0
  85. data/lib/rsmp/tlc/modules/signal_groups.rb +109 -0
  86. data/lib/rsmp/tlc/modules/startup_sequence.rb +22 -0
  87. data/lib/rsmp/tlc/modules/system.rb +140 -0
  88. data/lib/rsmp/tlc/modules/traffic_data.rb +49 -0
  89. data/lib/rsmp/tlc/signal_group.rb +37 -41
  90. data/lib/rsmp/tlc/signal_plan.rb +14 -11
  91. data/lib/rsmp/tlc/signal_priority.rb +39 -35
  92. data/lib/rsmp/tlc/startup_sequence.rb +59 -0
  93. data/lib/rsmp/tlc/traffic_controller.rb +39 -1006
  94. data/lib/rsmp/tlc/traffic_controller_site.rb +58 -57
  95. data/lib/rsmp/version.rb +1 -1
  96. data/lib/rsmp.rb +86 -49
  97. data/rsmp.gemspec +24 -30
  98. metadata +87 -130
  99. data/lib/rsmp/archive.rb +0 -76
  100. data/lib/rsmp/collect/message_matchers.rb +0 -0
  101. data/lib/rsmp/component_base.rb +0 -87
  102. data/lib/rsmp/component_proxy.rb +0 -57
  103. data/lib/rsmp/components.rb +0 -65
  104. data/lib/rsmp/error.rb +0 -71
  105. data/lib/rsmp/logger.rb +0 -216
  106. data/lib/rsmp/proxy.rb +0 -695
  107. data/lib/rsmp/site.rb +0 -188
  108. data/lib/rsmp/site_proxy.rb +0 -389
  109. data/lib/rsmp/supervisor.rb +0 -287
  110. data/lib/rsmp/supervisor_proxy.rb +0 -516
  111. data/lib/rsmp/tlc/inputs.rb +0 -134
@@ -35,91 +35,103 @@ module RSMP
35
35
  attr_reader :matchers
36
36
 
37
37
  # Initialize with a list of wanted statuses
38
- def initialize proxy, want, options={}
39
- raise ArgumentError.new("num option cannot be used") if options[:num]
40
- super proxy, options
38
+ def initialize(proxy, want, options = {})
39
+ raise ArgumentError, 'num option cannot be used' if options[:num]
40
+
41
+ super(proxy, options)
41
42
  @matchers = want.map { |item| build_matcher item }
42
43
  end
43
44
 
44
45
  # Build a matcher object.
45
46
  # Sub-classes should override to use their own matcher classes.
46
- def build_matcher want
47
+ def build_matcher(want)
47
48
  Matcher.new want
48
49
  end
49
50
 
50
51
  # Get a results
51
- def matcher_result want
52
- matcher = @matchers.find { |q| q.want == want}
52
+ def matcher_result(want)
53
+ matcher = @matchers.find { |q| q.want == want }
53
54
  raise unless matcher
55
+
54
56
  matcher.got
55
57
  end
56
58
 
57
59
  # Get an array of the last item received for each matcher
58
60
  def reached
59
- @matchers.map { |matcher| matcher.got }.compact
61
+ @matchers.map(&:got).compact
60
62
  end
61
63
 
62
64
  # Get messages from results
63
65
  def messages
64
- @matchers.map { |matcher| matcher.message }.uniq.compact
66
+ @matchers.map(&:message).uniq.compact
65
67
  end
66
68
 
67
69
  # Return progress as completes matchers vs. total number of matchers
68
70
  def progress
69
71
  need = @matchers.size
70
- reached = @matchers.count { |matcher| matcher.done? }
72
+ reached = @matchers.count(&:done?)
71
73
  { need: need, reached: reached }
72
74
  end
73
75
 
74
76
  # Are there matchers left to type_match?
75
77
  def done?
76
- @matchers.all? { |matcher| matcher.done? }
78
+ @matchers.all?(&:done?)
77
79
  end
78
80
 
79
81
  # Get a simplified hash of matchers, with values set to either true or false,
80
82
  # indicating which matchers have been matched.
81
83
  def matcher_status
82
- @matchers.map { |matcher| [matcher.want, matcher.done?] }.to_h
84
+ @matchers.to_h { |matcher| [matcher.want, matcher.done?] }
83
85
  end
84
86
 
85
87
  # Get a simply array of bools, showing which matchers have been matched.
86
88
  def summary
87
- @matchers.map { |matcher| matcher.done? }
89
+ @matchers.map(&:done?)
90
+ end
91
+
92
+ def log_match_result(message, matched, matcher, item)
93
+ type = { true => 'match', false => 'mismatch' }[matched]
94
+ @distributor.log "#{@title.capitalize} #{message.m_id_short} collect #{type} #{matcher.want}, item #{item}",
95
+ level: :debug
96
+ end
97
+
98
+ def handle_match_result(matched, matcher, message, item)
99
+ return if matched.nil?
100
+
101
+ log_match_result(message, matched, matcher, item)
102
+ if matched == true
103
+ matcher.keep message, item
104
+ elsif matched == false
105
+ matcher.forget
106
+ end
88
107
  end
89
108
 
90
- # Check if a messages matches our criteria.
91
- # Match each matcher against each item in the message
92
- def perform_match message
93
- return false if super(message) == false
109
+ def perform_match(message)
110
+ return false if super == false
94
111
  return unless collecting?
95
- @matchers.each do |matcher| # look through matchers
96
- get_items(message).each do |item| # look through items in message
97
- matched = matcher.perform_match(item,message,@block)
98
- return unless collecting?
99
- if matched != nil
100
- type = {true=>'match',false=>'mismatch'}[matched]
101
- @distributor.log "#{@title.capitalize} #{message.m_id_short} collect #{type} #{matcher.want}, item #{item}", level: :debug
102
- if matched == true
103
- matcher.keep message, item
104
- elsif matched == false
105
- matcher.forget
106
- end
107
- end
112
+
113
+ @matchers.each do |matcher|
114
+ break unless collecting?
115
+
116
+ get_items(message).each do |item|
117
+ matched = matcher.perform_match(item, message, @block)
118
+ break unless collecting?
119
+
120
+ handle_match_result(matched, matcher, message, item)
108
121
  end
109
122
  end
110
123
  end
111
124
 
112
125
  # don't collect anything. Matcher will collect them instead
113
- def keep message
114
- end
126
+ def keep(message); end
115
127
 
116
128
  def describe
117
- @matchers.map {|q| q.want.to_s }
129
+ @matchers.map { |q| q.want.to_s }
118
130
  end
119
131
 
120
132
  # return a string that describes the attributes that we're looking for
121
133
  def describe_matcher
122
- "#{super} matching #{matcher_want_hash.to_s}"
134
+ "#{super} matching #{matcher_want_hash}"
123
135
  end
124
136
 
125
137
  # return a hash that describe the status of all matchers
@@ -128,33 +140,45 @@ module RSMP
128
140
  @matchers.each do |matcher|
129
141
  want = matcher.want
130
142
  if want['cCI']
131
- cCI = want['cCI']
132
- h[cCI] ||= {}
133
- cO = h['cO']
134
- n = h['n']
135
- v = h['v']
136
- h[cCI][cO] ||= {}
137
- h[cCI][cO][n] = v
143
+ process_command_matcher(h, matcher, want)
138
144
  elsif want['sCI']
139
- sCI = want['sCI']
140
- h[sCI] ||= {}
141
- n = want['n']
142
- s = want['s']
143
- if matcher.got && matcher.got['s']
144
- h[sCI][n] = { {s=>matcher.got['s']} => matcher.done? }
145
- else
146
- h[sCI][n] = { s=>nil }
147
- end
145
+ process_status_matcher(h, matcher, want)
148
146
  end
149
147
  end
150
148
  h
151
149
  end
152
150
 
151
+ private
152
+
153
+ def process_command_matcher(hash, _matcher, want)
154
+ cci = want['cCI']
155
+ hash[cci] ||= {}
156
+ co = want['cO']
157
+ n = want['n']
158
+ v = want['v']
159
+ hash[cci][co] ||= {}
160
+ hash[cci][co][n] = v
161
+ end
162
+
163
+ def process_status_matcher(hash, matcher, want)
164
+ sci = want['sCI']
165
+ hash[sci] ||= {}
166
+ n = want['n']
167
+ s = want['s']
168
+ hash[sci][n] = if matcher.got && matcher.got['s']
169
+ { { s => matcher.got['s'] } => matcher.done? }
170
+ else
171
+ { s => nil }
172
+ end
173
+ end
174
+
175
+ public
176
+
153
177
  # return a string that describe how many many messages have been collected
154
178
  def describe_progress
155
179
  num_matchers = @matchers.size
156
- num_matched = @matchers.count { |matcher| matcher.done? }
157
- ".. Matched #{num_matched}/#{num_matchers} with #{progress_hash.to_s}"
180
+ num_matched = @matchers.count(&:done?)
181
+ ".. Matched #{num_matched}/#{num_matchers} with #{progress_hash}"
158
182
  end
159
183
 
160
184
  def matcher_want_hash
@@ -162,44 +186,59 @@ module RSMP
162
186
  @matchers.each do |matcher|
163
187
  item = matcher.want
164
188
  if item['cCI']
165
- cCI = item['cCI']
166
- h[cCI] ||= {}
167
- cO = item['cO']
168
- h[cCI][cO] ||= {}
169
- n = item['n']
170
- v = item['v']
171
- h[cCI][cO][n] = v || :any
189
+ add_command_want_to_hash(h, item)
172
190
  elsif item['sCI']
173
- sCI = item['sCI']
174
- h[sCI] ||= {}
175
- n = item['n']
176
- s = item['s']
177
- h[sCI][n] = s || :any
191
+ add_status_want_to_hash(h, item)
178
192
  end
179
193
  end
180
194
  h
181
195
  end
182
196
 
183
- # return a hash that describe the end result
197
+ def add_command_want_to_hash(hash, item)
198
+ cci = item['cCI']
199
+ hash[cci] ||= {}
200
+ co = item['cO']
201
+ hash[cci][co] ||= {}
202
+ n = item['n']
203
+ v = item['v']
204
+ hash[cci][co][n] = v || :any
205
+ end
206
+
207
+ def add_status_want_to_hash(hash, item)
208
+ sci = item['sCI']
209
+ hash[sci] ||= {}
210
+ n = item['n']
211
+ s = item['s']
212
+ hash[sci][n] = s || :any
213
+ end
214
+
215
+ def add_command_result(hash, want, got)
216
+ cci = want['cCI']
217
+ hash[cci] ||= {}
218
+ co = want['cO']
219
+ hash[cci][co] ||= {}
220
+ n = want['n']
221
+ v = got ? got['v'] : nil
222
+ hash[cci][co][n] = v
223
+ end
224
+
225
+ def add_status_result(hash, want, got)
226
+ sci = want['sCI']
227
+ hash[sci] ||= {}
228
+ n = want['n']
229
+ s = got ? got['s'] : nil
230
+ hash[sci][n] = s
231
+ end
232
+
184
233
  def matcher_got_hash
185
234
  h = {}
186
235
  @matchers.each do |matcher|
187
236
  want = matcher.want
188
237
  got = matcher.got
189
238
  if want['cCI']
190
- cCI = want['cCI']
191
- h[cCI] ||= {}
192
- cO = want['cO']
193
- h[cCI][cO] ||= {}
194
- n = want['n']
195
- v = got ? got['v'] : nil
196
- h[cCI][cO][n] = v
239
+ add_command_result(h, want, got)
197
240
  elsif want['sCI']
198
- sCI = want['sCI']
199
- h[sCI] ||= {}
200
- n = want['n']
201
- s = got ? got['s'] : nil
202
- h[sCI][n] = s
241
+ add_status_result(h, want, got)
203
242
  end
204
243
  end
205
244
  h
@@ -207,7 +246,7 @@ module RSMP
207
246
 
208
247
  # log when we end collecting
209
248
  def log_complete
210
- @distributor.log "#{identifier}: Completed with #{matcher_got_hash.to_s}", level: :collect
249
+ @distributor.log "#{identifier}: Completed with #{matcher_got_hash}", level: :collect
211
250
  end
212
251
  end
213
252
  end
@@ -1,24 +1,24 @@
1
1
  module RSMP
2
2
  # Base class for waiting for status updates or responses
3
3
  class StatusCollector < StateCollector
4
- def initialize proxy, want, options={}
4
+ def initialize(proxy, want, options = {})
5
5
  type = []
6
6
  type << 'StatusUpdate' unless options[:updates] == false
7
7
  type << 'StatusResponse' unless options[:reponses] == false
8
8
 
9
- super proxy, want, options.merge(
9
+ super(proxy, want, options.merge(
10
10
  title: 'status response',
11
11
  filter: RSMP::Filter.new(ingoing: true, outgoing: false, type: type)
12
- )
12
+ ))
13
13
  end
14
14
 
15
- def build_matcher want
15
+ def build_matcher(want)
16
16
  RSMP::StatusMatcher.new want
17
17
  end
18
18
 
19
19
  # Get items, in our case status values
20
- def get_items message
20
+ def get_items(message)
21
21
  message.attributes['sS'] || []
22
22
  end
23
23
  end
24
- end
24
+ end
@@ -1,12 +1,16 @@
1
1
  module RSMP
2
- # Match a specific status response or update
3
2
  class StatusMatcher < Matcher
4
- # Match a status value against a matcher
5
- def match? item
6
- return nil if @want['sCI'] && @want['sCI'] != item['sCI']
7
- return nil if @want['cO'] && @want['cO'] != item['cO']
8
- return nil if @want['n'] && @want['n'] != item['n']
3
+ def match_code?(item)
4
+ return false if @want['sCI'] && @want['sCI'] != item['sCI']
5
+ return false if @want['cO'] && @want['cO'] != item['cO']
6
+ return false if @want['n'] && @want['n'] != item['n']
7
+
8
+ true
9
+ end
10
+
11
+ def match_value?(item)
9
12
  return false if @want['q'] && @want['q'] != item['q']
13
+
10
14
  if @want['s'].is_a? Regexp
11
15
  return false if item['s'] !~ @want['s']
12
16
  elsif @want['s']
@@ -14,5 +18,11 @@ module RSMP
14
18
  end
15
19
  true
16
20
  end
21
+
22
+ def match?(item)
23
+ return nil unless match_code?(item)
24
+
25
+ match_value?(item)
26
+ end
17
27
  end
18
- end
28
+ end
@@ -1,5 +1,4 @@
1
1
  module RSMP
2
-
3
2
  # The state of an alarm on a component.
4
3
  # The alarm state is for a particular alarm code,
5
4
  # a component typically have an alarm state for each
@@ -8,10 +7,8 @@ module RSMP
8
7
  class AlarmState
9
8
  attr_reader :component_id, :code, :acknowledged, :suspended, :active, :timestamp, :category, :priority, :rvs
10
9
 
11
- def self.create_from_message component, message
12
- self.new(
13
- component: component,
14
- code: message.attribute("aCId"),
10
+ def self.create_from_message(component, message)
11
+ options = {
15
12
  timestamp: RSMP::Clock.parse(message.attribute('aTs')),
16
13
  acknowledged: message.attribute('ack') == 'Acknowledged',
17
14
  suspended: message.attribute('aS') == 'Suspended',
@@ -19,22 +16,21 @@ module RSMP
19
16
  category: message.attribute('cat'),
20
17
  priority: message.attribute('pri').to_i,
21
18
  rvs: message.attribute('rvs')
22
- )
19
+ }
20
+ new(component: component, code: message.attribute('aCId'), **options)
23
21
  end
24
22
 
25
- def initialize component:, code:,
26
- suspended: false, acknowledged: false, active: false, timestamp: nil,
27
- category: 'D', priority: 2, rvs: []
23
+ def initialize(component:, code:, **options)
28
24
  @component = component
29
25
  @component_id = component.c_id
30
26
  @code = code
31
- @suspended = !!suspended
32
- @acknowledged = !!acknowledged
33
- @active = !!active
34
- @timestamp = timestamp
35
- @category = category || 'D'
36
- @priority = priority || 2
37
- @rvs = rvs
27
+ @suspended = !!options[:suspended]
28
+ @acknowledged = !!options[:acknowledged]
29
+ @active = !!options[:active]
30
+ @timestamp = options[:timestamp]
31
+ @category = options[:category] || 'D'
32
+ @priority = options[:priority] || 2
33
+ @rvs = options[:rvs] || []
38
34
  end
39
35
 
40
36
  def to_hash
@@ -52,19 +48,22 @@ module RSMP
52
48
  end
53
49
 
54
50
  def acknowledge
55
- change, @acknowledged = !@acknowledged, true
51
+ change = !@acknowledged
52
+ @acknowledged = true
56
53
  update_timestamp if change
57
54
  change
58
55
  end
59
56
 
60
57
  def suspend
61
- change, @suspended = !@suspended, true
58
+ change = !@suspended
59
+ @suspended = true
62
60
  update_timestamp if change
63
61
  change
64
62
  end
65
63
 
66
64
  def resume
67
- change, @suspended = @suspended, false
65
+ change = @suspended
66
+ @suspended = false
68
67
  update_timestamp if change
69
68
  change
70
69
  end
@@ -73,29 +72,33 @@ module RSMP
73
72
  # is when it's activated. See:
74
73
  # https://rsmp-nordic.org/rsmp_specifications/core/3.2.0/applicability/basic_structure.html#alarm-status
75
74
  def activate
76
- change, @active, @acknowledged = !@active, true, false
75
+ change = !@active
76
+ @active = true
77
+ @acknowledged = false
77
78
  update_timestamp if change
78
79
  change
79
80
  end
80
81
 
81
82
  def deactivate
82
- change, @active = @active, false
83
+ change = @active
84
+ @active = false
83
85
  update_timestamp if change
84
86
  change
85
87
  end
86
-
88
+
87
89
  def update_timestamp
88
90
  @timestamp = @component.now
89
91
  end
90
92
 
91
- def differ_from_message? message
92
- return true if RSMP::Clock.to_s(@timestamp) != message.attribute('aTs')
93
- return true if message.attribute('ack') && @acknowledged != (message.attribute('ack').downcase == 'acknowledged')
94
- return true if message.attribute('sS') && @suspended != (message.attribute('sS').downcase == 'suspended')
95
- return true if message.attribute('aS') && @active != (message.attribute('aS').downcase == 'active')
96
- return true if message.attribute('cat') && @category != message.attribute('cat')
97
- return true if message.attribute('pri') && @priority != message.attribute('pri').to_i
98
- #return true @rvs = message.attribute('rvs')
93
+ def differ_from_message?(message)
94
+ return true if timestamp_differs?(message)
95
+ return true if acknowledgment_differs?(message)
96
+ return true if suspension_differs?(message)
97
+ return true if activity_differs?(message)
98
+ return true if category_differs?(message)
99
+ return true if priority_differs?(message)
100
+
101
+ # return true @rvs = message.attribute('rvs')
99
102
  false
100
103
  end
101
104
 
@@ -103,20 +106,20 @@ module RSMP
103
106
  @timestamp = nil
104
107
  end
105
108
 
106
- def older_message? message
107
- return false if @timestamp == nil
109
+ def older_message?(message)
110
+ return false if @timestamp.nil?
111
+
108
112
  RSMP::Clock.parse(message.attribute('aTs')) < @timestamp
109
113
  end
110
114
 
111
115
  # update from rsmp message
112
116
  # component id, alarm code and specialization are not updated
113
- def update_from_message message
117
+ def update_from_message(message)
114
118
  unless differ_from_message? message
115
- raise RepeatedAlarmError.new("no changes from previous alarm #{message.m_id_short}")
116
- end
117
- if older_message? message
118
- raise TimestampError.new("timestamp is earlier than previous alarm #{message.m_id_short}")
119
+ raise RepeatedAlarmError,
120
+ "no changes from previous alarm #{message.m_id_short}"
119
121
  end
122
+ raise TimestampError, "timestamp is earlier than previous alarm #{message.m_id_short}" if older_message? message
120
123
  ensure
121
124
  @timestamp = RSMP::Clock.parse message.attribute('aTs')
122
125
  @acknowledged = message.attribute('ack') == 'True'
@@ -126,5 +129,41 @@ module RSMP
126
129
  @priority = message.attribute('pri').to_i
127
130
  @rvs = message.attribute('rvs')
128
131
  end
132
+
133
+ private
134
+
135
+ def timestamp_differs?(message)
136
+ RSMP::Clock.to_s(@timestamp) != message.attribute('aTs')
137
+ end
138
+
139
+ def acknowledgment_differs?(message)
140
+ return false unless message.attribute('ack')
141
+
142
+ @acknowledged != (message.attribute('ack').downcase == 'acknowledged')
143
+ end
144
+
145
+ def suspension_differs?(message)
146
+ return false unless message.attribute('sS')
147
+
148
+ @suspended != (message.attribute('sS').downcase == 'suspended')
149
+ end
150
+
151
+ def activity_differs?(message)
152
+ return false unless message.attribute('aS')
153
+
154
+ @active != (message.attribute('aS').downcase == 'active')
155
+ end
156
+
157
+ def category_differs?(message)
158
+ return false unless message.attribute('cat')
159
+
160
+ @category != message.attribute('cat')
161
+ end
162
+
163
+ def priority_differs?(message)
164
+ return false unless message.attribute('pri')
165
+
166
+ @priority != message.attribute('pri').to_i
167
+ end
129
168
  end
130
169
  end
@@ -1,19 +1,19 @@
1
1
  module RSMP
2
2
  # RSMP component
3
3
  class Component < ComponentBase
4
- def initialize node:, id:, ntsOId: nil, xNId: nil, grouped: false
4
+ def initialize(node:, id:, ntsoid: nil, xnid: nil, grouped: false)
5
5
  super
6
6
  end
7
7
 
8
- def handle_command command_code, arg
9
- raise UnknownCommand.new "Command #{command_code} not implemented by #{self.class}"
8
+ def handle_command(command_code, _arg)
9
+ raise UnknownCommand, "Command #{command_code} not implemented by #{self.class}"
10
10
  end
11
11
 
12
- def get_status status_code, status_name=nil, options={}
13
- raise UnknownStatus.new "Status #{status_code}/#{status_name} not implemented by #{self.class}"
12
+ def get_status(status_code, status_name = nil, _options = {})
13
+ raise UnknownStatus, "Status #{status_code}/#{status_name} not implemented by #{self.class}"
14
14
  end
15
15
 
16
- def acknowledge_alarm alarm_code
16
+ def acknowledge_alarm(alarm_code)
17
17
  alarm = get_alarm_state alarm_code
18
18
  if alarm.acknowledge
19
19
  log "Acknowledging alarm #{alarm_code}", level: :info
@@ -23,17 +23,17 @@ module RSMP
23
23
  end
24
24
  end
25
25
 
26
- def suspend_alarm alarm_code
26
+ def suspend_alarm(alarm_code)
27
27
  alarm = get_alarm_state alarm_code
28
28
  if alarm.suspend
29
29
  log "Suspending alarm #{alarm_code}", level: :info
30
30
  @node.alarm_suspended_or_resumed alarm
31
31
  else
32
32
  log "Alarm #{alarm_code} already suspended", level: :info
33
- end
33
+ end
34
34
  end
35
35
 
36
- def resume_alarm alarm_code
36
+ def resume_alarm(alarm_code)
37
37
  alarm = get_alarm_state alarm_code
38
38
  if alarm.resume
39
39
  log "Resuming alarm #{alarm_code}", level: :info
@@ -43,22 +43,22 @@ module RSMP
43
43
  end
44
44
  end
45
45
 
46
- def activate_alarm alarm_code
46
+ def activate_alarm(alarm_code)
47
47
  alarm = get_alarm_state alarm_code
48
48
  return unless alarm.activate
49
+
49
50
  log "Activating alarm #{alarm_code}", level: :info
50
51
  @node.alarm_activated_or_deactivated alarm
51
52
  end
52
53
 
53
- def deactivate_alarm alarm_code
54
+ def deactivate_alarm(alarm_code)
54
55
  alarm = get_alarm_state alarm_code
55
56
  return unless alarm.deactivate
57
+
56
58
  log "Deactivating alarm #{alarm_code}", level: :info
57
59
  @node.alarm_activated_or_deactivated alarm
58
60
  end
59
61
 
60
- def status_updates_sent
61
- end
62
-
62
+ def status_updates_sent; end
63
63
  end
64
- end
64
+ end