rsmp 0.11.7 → 0.12.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/config/tlc.yaml +4 -1
- data/lib/rsmp/alarm_state.rb +25 -0
- data/lib/rsmp/collect/collector.rb +1 -0
- data/lib/rsmp/component.rb +24 -159
- data/lib/rsmp/component_base.rb +69 -0
- data/lib/rsmp/component_proxy.rb +67 -0
- data/lib/rsmp/components.rb +3 -3
- data/lib/rsmp/message.rb +5 -5
- data/lib/rsmp/proxy.rb +1 -0
- data/lib/rsmp/site.rb +22 -2
- data/lib/rsmp/site_proxy.rb +39 -11
- data/lib/rsmp/supervisor_proxy.rb +57 -28
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +3 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 957caaa3191011a85a0284ef530434184e15620b7762e4971a8547f45a955ec3
|
4
|
+
data.tar.gz: 8905eb13b36d26d6c50fe0e4466f40e2f742d18384e93bc2b7e67d2e77975441
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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
@@ -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
|
data/lib/rsmp/component.rb
CHANGED
@@ -1,72 +1,10 @@
|
|
1
1
|
module RSMP
|
2
2
|
|
3
|
-
#
|
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
|
-
|
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
|
-
|
81
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
#
|
166
|
-
|
167
|
-
|
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
|
data/lib/rsmp/components.rb
CHANGED
@@ -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
|
-
|
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
|
64
|
+
when /Issue/i
|
65
65
|
AlarmIssue.new attributes
|
66
|
-
when
|
66
|
+
when /Request/i
|
67
67
|
AlarmRequest.new attributes
|
68
|
-
when
|
68
|
+
when /Acknowledge/i
|
69
69
|
AlarmAcknowledged.new attributes
|
70
|
-
when
|
70
|
+
when /Suspend/i
|
71
71
|
AlarmSuspend.new attributes
|
72
|
-
when
|
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
|
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.
|
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
|
data/lib/rsmp/site_proxy.rb
CHANGED
@@ -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.
|
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
|
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.
|
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
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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.
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
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
|
-
|
266
|
-
@status_subscriptions[
|
267
|
-
update_list[
|
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[
|
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[
|
277
|
-
update_list[
|
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
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.
|
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-
|
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
|