rsmp 0.11.7 → 0.12.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 +4 -4
- data/Gemfile.lock +2 -2
- data/config/tlc.yaml +4 -1
- data/lib/rsmp/alarm_state.rb +33 -0
- data/lib/rsmp/collect/collector.rb +1 -0
- data/lib/rsmp/component.rb +29 -160
- 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 +38 -11
- data/lib/rsmp/supervisor_proxy.rb +57 -28
- data/lib/rsmp/tlc/traffic_controller.rb +2 -2
- 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: 34c922ce1e22f31aabc3b476f1aa3d9a6eeaa4b89e4550255f237c5f5810b4e8
|
4
|
+
data.tar.gz: 95590c49d1ba70af34dfd913808239cea39b5dae0324003deb921c21a0bf0aa2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d1c8bfd469ae45764292bf3e2615bf95b1904229d34c2df0eb18ad496a7d1b6ecfd0aeff594f07a0bbf59c34d7b6247fc566e1a94d65412f78719e3f4f42b1b
|
7
|
+
data.tar.gz: 66edc8044373bca92cc52641f49c78f3336d4e5c6caa8615a8da05aad6176d8e2c6161a5d835716fe7feac38948a059b4de6ec1f772de3c1f992d8c7f76bc8e0
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rsmp (0.
|
4
|
+
rsmp (0.12.2)
|
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,33 @@
|
|
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
|
+
|
25
|
+
def activate
|
26
|
+
@active = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def deactivate
|
30
|
+
@active = false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
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,109 +15,40 @@ 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
|
-
|
22
|
+
def suspend_alarm alarm_code
|
23
|
+
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
|
125
28
|
end
|
126
29
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
144
|
-
end
|
30
|
+
def resume_alarm alarm_code
|
31
|
+
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
|
145
36
|
end
|
146
37
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
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
|
162
|
-
end
|
38
|
+
def activate_alarm alarm_code
|
39
|
+
alarm = get_alarm_state alarm_code
|
40
|
+
return if alarm.active
|
41
|
+
log "Activating alarm #{alarm_code}", level: :info
|
42
|
+
alarm.activate
|
43
|
+
@node.alarm_changed alarm
|
163
44
|
end
|
164
45
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
46
|
+
def deactivate_alarm alarm_code
|
47
|
+
alarm = get_alarm_state alarm_code
|
48
|
+
return unless alarm.active
|
49
|
+
log "Deactivating alarm #{alarm_code}", level: :info
|
50
|
+
alarm.deactivate
|
51
|
+
@node.alarm_changed alarm
|
182
52
|
end
|
183
|
-
|
184
53
|
end
|
185
54
|
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_changed 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,22 @@ 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
|
+
|
219
231
|
component = find_component component_id
|
220
|
-
component.
|
232
|
+
component.allow_repeat_updates subscribe_list
|
221
233
|
|
222
234
|
message = RSMP::StatusSubscribe.new({
|
223
235
|
"ntsOId" => '',
|
@@ -236,15 +248,24 @@ module RSMP
|
|
236
248
|
end
|
237
249
|
|
238
250
|
def unsubscribe_to_status component_id, status_list, options={}
|
239
|
-
component = find_component component_id
|
240
|
-
component.handle_status_subscribe status_list
|
241
|
-
|
242
251
|
validate_ready 'unsubscribe to status'
|
252
|
+
|
253
|
+
# update our subcription list
|
254
|
+
status_list.each do |item|
|
255
|
+
sCI = item["sCI"]
|
256
|
+
n = item["n"]
|
257
|
+
if @status_subscriptions.dig(component_id,sCI,n)
|
258
|
+
@status_subscriptions[component_id][sCI].delete n
|
259
|
+
@status_subscriptions[component_id].delete(sCI) if @status_subscriptions[component_id][sCI].empty?
|
260
|
+
@status_subscriptions.delete(component_id) if @status_subscriptions[component_id].empty?
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
243
264
|
message = RSMP::StatusUnsubscribe.new({
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
265
|
+
"ntsOId" => '',
|
266
|
+
"xNId" => '',
|
267
|
+
"cId" => component_id,
|
268
|
+
"sS" => status_list
|
248
269
|
})
|
249
270
|
send_message message, validate: options[:validate]
|
250
271
|
message
|
@@ -252,7 +273,8 @@ module RSMP
|
|
252
273
|
|
253
274
|
def process_status_update message
|
254
275
|
component = find_component message.attribute("cId")
|
255
|
-
component.
|
276
|
+
component.check_repeat_values message, @status_subscriptions
|
277
|
+
component.store_status message
|
256
278
|
log "Received #{message.type}", message: message, level: :log
|
257
279
|
acknowledge message
|
258
280
|
end
|
@@ -357,5 +379,10 @@ module RSMP
|
|
357
379
|
@supervisor.notify_error e, options if @supervisor
|
358
380
|
distribute_error e, options
|
359
381
|
end
|
382
|
+
|
383
|
+
def build_component id:, type:, settings:{}
|
384
|
+
ComponentProxy.new id:id, node: self, grouped: type=='main'
|
385
|
+
end
|
386
|
+
|
360
387
|
end
|
361
388
|
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"]
|
@@ -264,10 +264,10 @@ module RSMP
|
|
264
264
|
alarm_code = actions['raise']
|
265
265
|
if change
|
266
266
|
log "Activating alarm #{alarm_code}, because input #{input} was activated", level: :info
|
267
|
-
|
267
|
+
activate_alarm alarm_code
|
268
268
|
else
|
269
269
|
log "Deactivating alarm #{alarm_code}, because input #{input} was deactivated", level: :info
|
270
|
-
|
270
|
+
deactivate_alarm alarm_code
|
271
271
|
end
|
272
272
|
end
|
273
273
|
end
|
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.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-
|
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
|