rsmp 0.2.3 → 0.3.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 +1 -1
- data/lib/rsmp/collector.rb +77 -41
- data/lib/rsmp/matcher.rb +183 -0
- data/lib/rsmp/proxy.rb +1 -0
- data/lib/rsmp/site_proxy.rb +48 -57
- data/lib/rsmp/site_proxy_wait.rb +0 -184
- data/lib/rsmp/supervisor.rb +6 -1
- data/lib/rsmp/supervisor_proxy.rb +1 -1
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a3b1b8a67bdc7a3c8887ea606d3539d934121d99597f1a77f697d8259daba97
|
4
|
+
data.tar.gz: 03f420b48a4eff2f3b6978eed5053a838a87da4f507eb2ddb2f0c15cb0e3e7b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fef0b5ea4b2e5481d3be7992e76c7cddb1d20db49df06c86d9b90faa37b56d458fde9cba36e14ea085e96e67f61dbe67f1a82a49401d360cb48900c6138b27d
|
7
|
+
data.tar.gz: a14a735fccf39e8584e3d9ab43a1fdb727c279f395c138d9c7c50f7a8d491265c0aeb3aa67ee071642195bd7a15e0339de4785b5230bbed624965ef3619a231e
|
data/Gemfile.lock
CHANGED
data/lib/rsmp/collector.rb
CHANGED
@@ -1,21 +1,19 @@
|
|
1
|
-
# Collects matching ingoing and/or outgoing messages and
|
2
|
-
# wakes up the client once the desired amount has been collected.
|
3
|
-
# Can listen for ingoing and/or outgoing messages.
|
4
|
-
|
5
1
|
module RSMP
|
6
|
-
class Collector < Listener
|
7
2
|
|
3
|
+
# Collects ingoing and/or outgoing messages.
|
4
|
+
# Can filter by message type and wakes up the client once the desired number of messages has been collected.
|
5
|
+
class Collector < Listener
|
8
6
|
attr_reader :condition, :messages, :done
|
9
7
|
|
10
8
|
def initialize proxy, options={}
|
11
9
|
super proxy, options
|
10
|
+
@options = options.clone
|
12
11
|
@ingoing = options[:ingoing] == nil ? true : options[:ingoing]
|
13
12
|
@outgoing = options[:outgoing] == nil ? false : options[:outgoing]
|
14
|
-
@messages = []
|
15
13
|
@condition = Async::Notification.new
|
16
|
-
@
|
17
|
-
@options
|
18
|
-
|
14
|
+
@title = options[:title] || [@options[:type]].flatten.join('/')
|
15
|
+
@options[:timeout] ||= 1
|
16
|
+
reset
|
19
17
|
end
|
20
18
|
|
21
19
|
def inspect
|
@@ -34,17 +32,9 @@ module RSMP
|
|
34
32
|
@condition.wait
|
35
33
|
end
|
36
34
|
|
37
|
-
def collect_for task, duration
|
38
|
-
siphon do
|
39
|
-
task.sleep duration
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
35
|
def collect task, options={}, &block
|
44
|
-
@
|
45
|
-
@options[:timeout] = options[:timeout] if options[:timeout]
|
36
|
+
@options.merge! options
|
46
37
|
@block = block
|
47
|
-
|
48
38
|
unless @done
|
49
39
|
listen do
|
50
40
|
task.with_timeout(@options[:timeout]) do
|
@@ -52,51 +42,97 @@ module RSMP
|
|
52
42
|
end
|
53
43
|
end
|
54
44
|
end
|
45
|
+
return @error if @error
|
46
|
+
self
|
47
|
+
rescue Async::TimeoutError
|
48
|
+
str = "Did not receive #{@title}"
|
49
|
+
str << " in response to #{options[:m_id]}" if options[:m_id]
|
50
|
+
str << " within #{@options[:timeout]}s"
|
51
|
+
raise RSMP::TimeoutError.new str
|
52
|
+
end
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
@messages = @messages.first @num # return array, but ensure we never return more than requested
|
60
|
-
end
|
61
|
-
@messages
|
54
|
+
def result
|
55
|
+
return @messages.first if @options[:num] == 1 # if one message was requested, return it instead of array
|
56
|
+
@messages.first @options[:num] # return array, but ensure we never return more than requested
|
62
57
|
end
|
63
58
|
|
64
59
|
def reset
|
65
|
-
@
|
60
|
+
@messages = []
|
61
|
+
@error = nil
|
66
62
|
@done = false
|
67
63
|
end
|
68
64
|
|
65
|
+
# Check for MessageNotAck
|
66
|
+
# If the original request identified by @m_id is rejected, set the result to an exception,
|
67
|
+
# which will be returned by the async task and stored as the task result.
|
68
|
+
# When the parent task call wait() on the task, the exception will be raised in the parent task.
|
69
|
+
def check_not_ack message
|
70
|
+
return unless @options[:m_id]
|
71
|
+
if message.is_a?(MessageNotAck)
|
72
|
+
if message.attribute('oMId') == @options[:m_id]
|
73
|
+
m_id_short = RSMP::Message.shorten_m_id @options[:m_id], 8
|
74
|
+
@error = RSMP::MessageRejected.new("#{@title} #{m_id_short} was rejected: #{message.attribute('rea')}")
|
75
|
+
complete
|
76
|
+
end
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Handle message. and return true when we're done collecting
|
69
82
|
def notify message
|
70
83
|
raise ArgumentError unless message
|
84
|
+
check_not_ack(message)
|
71
85
|
return true if @done
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
86
|
+
check_match message
|
87
|
+
complete if done?
|
88
|
+
@done
|
89
|
+
end
|
90
|
+
|
91
|
+
# Match message against our collection criteria
|
92
|
+
def check_match message
|
93
|
+
matched = match? message
|
94
|
+
if matched == true
|
95
|
+
keep message
|
96
|
+
elsif matched == false
|
97
|
+
forget message
|
81
98
|
end
|
82
99
|
end
|
83
100
|
|
84
|
-
def
|
85
|
-
|
101
|
+
def done?
|
102
|
+
@options[:num] && @messages.size >= @options[:num]
|
103
|
+
end
|
104
|
+
|
105
|
+
def complete
|
106
|
+
@done = true
|
107
|
+
@proxy.remove_listener self
|
108
|
+
@condition.signal
|
109
|
+
end
|
110
|
+
|
111
|
+
def keep message
|
112
|
+
@messages << message
|
113
|
+
end
|
114
|
+
|
115
|
+
def forget message
|
116
|
+
@messages.delete message
|
117
|
+
end
|
86
118
|
|
119
|
+
def match? message
|
120
|
+
raise ArgumentError unless message
|
121
|
+
return if message.direction == :in && @ingoing == false
|
122
|
+
return if message.direction == :out && @outgoing == false
|
87
123
|
if @options[:type]
|
88
|
-
return
|
124
|
+
return if message == nil
|
89
125
|
if @options[:type].is_a? Array
|
90
|
-
return
|
126
|
+
return unless @options[:type].include? message.type
|
91
127
|
else
|
92
|
-
return
|
128
|
+
return unless message.type == @options[:type]
|
93
129
|
end
|
94
130
|
end
|
95
131
|
if @options[:component]
|
96
|
-
return
|
132
|
+
return if message.attributes['cId'] && message.attributes['cId'] != @options[:component]
|
97
133
|
end
|
98
134
|
if @block
|
99
|
-
return
|
135
|
+
return if @block.call(message) == false
|
100
136
|
end
|
101
137
|
true
|
102
138
|
end
|
data/lib/rsmp/matcher.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
module RSMP
|
2
|
+
|
3
|
+
# Base class for waiting for specific status or command responses, specified by
|
4
|
+
# a list of queries. Queries are defined as an array of hashes, e.g
|
5
|
+
# [
|
6
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"securityCode", "v"=>"1111"},
|
7
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"year", "v"=>"2020"},
|
8
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/}
|
9
|
+
# ]
|
10
|
+
#
|
11
|
+
# Note that queries can contain regex patterns for values, like /\d+/ in the example above.
|
12
|
+
#
|
13
|
+
# When an input messages is received it typically contains several items, eg:
|
14
|
+
# [
|
15
|
+
# {"cCI"=>"M0104", "n"=>"month", "v"=>"9", "age"=>"recent"},
|
16
|
+
# {"cCI"=>"M0104", "n"=>"day", "v"=>"29", "age"=>"recent"},
|
17
|
+
# {"cCI"=>"M0104", "n"=>"hour", "v"=>"17", "age"=>"recent"}
|
18
|
+
# ]
|
19
|
+
#
|
20
|
+
# Each input item is matched against each of the queries.
|
21
|
+
# If a match is found, it's stored in the @results hash, with the query as the key,
|
22
|
+
# and a mesage and status as the key. In the example above, this query:
|
23
|
+
#
|
24
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/}
|
25
|
+
#
|
26
|
+
# matches this input:
|
27
|
+
#
|
28
|
+
# {"cCI"=>"M0104", "n"=>"month", "v"=>"9", "age"=>"recent"}
|
29
|
+
#
|
30
|
+
# And the result is stored as:
|
31
|
+
# {
|
32
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/} =>
|
33
|
+
# { <StatusResponse message>, {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>"9"} }
|
34
|
+
# }
|
35
|
+
#
|
36
|
+
#
|
37
|
+
class Matcher < Collector
|
38
|
+
|
39
|
+
# Initialize with a list a wanted statuses
|
40
|
+
def initialize proxy, want, options={}
|
41
|
+
super proxy, options.merge( ingoing: true, outgoing: false)
|
42
|
+
@queries = {}
|
43
|
+
want.each do |query|
|
44
|
+
@queries[query] = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Get the results, as a hash of queries => results
|
49
|
+
def result
|
50
|
+
@queries
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get messages from results
|
54
|
+
def messages
|
55
|
+
@queries.map { |query,result| result[:message] }.uniq
|
56
|
+
end
|
57
|
+
|
58
|
+
# get items from results
|
59
|
+
def items
|
60
|
+
@queries.map { |query,result| result[:item] }.uniq
|
61
|
+
end
|
62
|
+
|
63
|
+
# Queries left to match?
|
64
|
+
def done?
|
65
|
+
@queries.values.all? { |result| result != nil }
|
66
|
+
end
|
67
|
+
|
68
|
+
# Mark a query as matched, by linking it to the matched item and message
|
69
|
+
def keep query, message, item
|
70
|
+
@queries[query] = { message:message, item:item }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Mark a query as not matched
|
74
|
+
def forget query
|
75
|
+
@queries[query] = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
# Check if a messages is wanted.
|
79
|
+
# Returns true when we found all that we want.
|
80
|
+
def check_match message
|
81
|
+
return unless match?(message)
|
82
|
+
@queries.keys.each do |query| # look through queries
|
83
|
+
get_items(message).each do |item| # look through status items in message
|
84
|
+
break if check_item_match message, query, item
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Check if an item matches, and mark query as matched/unmatched accordingly.
|
90
|
+
def check_item_match message, query, item
|
91
|
+
matched = match_item? query, item
|
92
|
+
if matched == true
|
93
|
+
keep query, message, item
|
94
|
+
true
|
95
|
+
elsif matched == false
|
96
|
+
forget query
|
97
|
+
true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Class for waiting for specific command responses
|
103
|
+
class CommandResponseMatcher < Matcher
|
104
|
+
def initialize proxy, want, options={}
|
105
|
+
super proxy, want, options.merge(
|
106
|
+
type: ['CommandResponse','MessageNotAck'],
|
107
|
+
title:'command request'
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_items message
|
112
|
+
message.attributes['rvs']
|
113
|
+
end
|
114
|
+
|
115
|
+
# Match an item against a query
|
116
|
+
def match_item? query, item
|
117
|
+
return nil if query['cCI'] && query['cCI'] != item['cCI']
|
118
|
+
return nil if query['n'] && query['n'] != item['n']
|
119
|
+
if query['v'].is_a? Regexp
|
120
|
+
return false if query['v'] && item['v'] !~ query['v']
|
121
|
+
else
|
122
|
+
return false if query['v'] && item['v'] != query['v']
|
123
|
+
end
|
124
|
+
true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Base class for waiting for status updates or responses
|
129
|
+
class StatusUpdateOrResponseMatcher < Matcher
|
130
|
+
def initialize proxy, want, options={}
|
131
|
+
super proxy, want, options.merge
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_items message
|
135
|
+
message.attributes['sS']
|
136
|
+
end
|
137
|
+
|
138
|
+
# Match an item against a query
|
139
|
+
def match_item? query, item
|
140
|
+
return nil if query['sCI'] && query['sCI'] != item['sCI']
|
141
|
+
return nil if query['cO'] && query['cO'] != item['cO']
|
142
|
+
return nil if query['n'] && query['n'] != item['n']
|
143
|
+
return false if query['q'] && query['q'] != item['q']
|
144
|
+
if query['s'].is_a? Regexp
|
145
|
+
return false if query['s'] && item['s'] !~ query['s']
|
146
|
+
else
|
147
|
+
return false if query['s'] && item['s'] != query['s']
|
148
|
+
end
|
149
|
+
true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Class for waiting for specific status responses
|
154
|
+
class StatusResponseMatcher < StatusUpdateOrResponseMatcher
|
155
|
+
def initialize proxy, want, options={}
|
156
|
+
super proxy, want, options.merge(
|
157
|
+
type: ['StatusResponse','MessageNotAck'],
|
158
|
+
title: 'status request'
|
159
|
+
)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Class for waiting for specific status responses
|
164
|
+
class StatusUpdateMatcher < StatusUpdateOrResponseMatcher
|
165
|
+
def initialize proxy, want, options={}
|
166
|
+
super proxy, want, options.merge(
|
167
|
+
type: ['StatusUpdate','MessageNotAck'],
|
168
|
+
title:'status subscription'
|
169
|
+
)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Class for waiting for an aggregated status response
|
174
|
+
class AggregatedStatusMatcher < Collector
|
175
|
+
def initialize proxy, options={}
|
176
|
+
super proxy, options.merge(
|
177
|
+
num: 1,
|
178
|
+
type: ['AggregatedStatus','MessageNotAck'],
|
179
|
+
title: 'aggregated status request'
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
data/lib/rsmp/proxy.rb
CHANGED
data/lib/rsmp/site_proxy.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
module RSMP
|
4
4
|
class SiteProxy < Proxy
|
5
5
|
include Components
|
6
|
-
include SiteProxyWait
|
7
6
|
|
8
7
|
attr_reader :supervisor, :site_id
|
9
8
|
|
@@ -43,7 +42,7 @@ module RSMP
|
|
43
42
|
|
44
43
|
def connection_complete
|
45
44
|
super
|
46
|
-
|
45
|
+
sanitized_sxl_version = RSMP::Schemer.sanitize_version(@site_sxl_version)
|
47
46
|
log "Connection to site #{@site_id} established, using core #{@rsmp_version}, #{@sxl} #{sanitized_sxl_version}", level: :info
|
48
47
|
end
|
49
48
|
|
@@ -86,7 +85,6 @@ module RSMP
|
|
86
85
|
acknowledge message
|
87
86
|
send_version @site_id, rsmp_versions
|
88
87
|
@version_determined = true
|
89
|
-
|
90
88
|
end
|
91
89
|
|
92
90
|
def validate_ready action
|
@@ -104,9 +102,8 @@ module RSMP
|
|
104
102
|
"mId" => m_id
|
105
103
|
})
|
106
104
|
if options[:collect]
|
107
|
-
result = nil
|
108
105
|
task = @task.async do |task|
|
109
|
-
wait_for_aggregated_status task, options[:collect]
|
106
|
+
wait_for_aggregated_status task, options[:collect].merge(m_id: m_id)
|
110
107
|
end
|
111
108
|
send_message message, validate: options[:validate]
|
112
109
|
return message, task.wait
|
@@ -178,7 +175,7 @@ module RSMP
|
|
178
175
|
m_id = options[:m_id] || RSMP::Message.make_m_id
|
179
176
|
|
180
177
|
# additional items can be used when verifying the response,
|
181
|
-
# but must
|
178
|
+
# but must be removed from the request
|
182
179
|
request_list = status_list.map { |item| item.slice('sCI','n') }
|
183
180
|
|
184
181
|
message = RSMP::StatusRequest.new({
|
@@ -188,23 +185,8 @@ module RSMP
|
|
188
185
|
"sS" => request_list,
|
189
186
|
"mId" => m_id
|
190
187
|
})
|
191
|
-
|
192
|
-
|
193
|
-
task = @task.async do |task|
|
194
|
-
collect_options = options[:collect].merge status_list: status_list
|
195
|
-
collect_status_responses task, collect_options, m_id
|
196
|
-
end
|
197
|
-
send_message message, validate: options[:validate]
|
198
|
-
|
199
|
-
# task.wait return the result of the task. if the task raised an exception
|
200
|
-
# it will be reraised. but that mechanish does not work if multiple values
|
201
|
-
# are returned. so manually raise if first element is an exception
|
202
|
-
result = task.wait
|
203
|
-
raise result.first if result.first.is_a? Exception
|
204
|
-
return message, *result
|
205
|
-
else
|
206
|
-
send_message message, validate: options[:validate]
|
207
|
-
message
|
188
|
+
send_while_collecting message, options do |task|
|
189
|
+
collect_status_responses task, status_list, options[:collect].merge(m_id: m_id)
|
208
190
|
end
|
209
191
|
end
|
210
192
|
|
@@ -213,6 +195,13 @@ module RSMP
|
|
213
195
|
acknowledge message
|
214
196
|
end
|
215
197
|
|
198
|
+
def send_while_collecting message, options, &block
|
199
|
+
task = @task.async { |task| yield task } if options[:collect]
|
200
|
+
send_message message, validate: options[:validate]
|
201
|
+
return message, task.wait if task
|
202
|
+
message
|
203
|
+
end
|
204
|
+
|
216
205
|
def subscribe_to_status component, status_list, options={}
|
217
206
|
validate_ready 'subscribe to status'
|
218
207
|
m_id = options[:m_id] || RSMP::Message.make_m_id
|
@@ -228,23 +217,8 @@ module RSMP
|
|
228
217
|
"sS" => subscribe_list,
|
229
218
|
'mId' => m_id
|
230
219
|
})
|
231
|
-
|
232
|
-
|
233
|
-
task = @task.async do |task|
|
234
|
-
collect_options = options[:collect].merge status_list: status_list
|
235
|
-
collect_status_updates task, collect_options, m_id
|
236
|
-
end
|
237
|
-
send_message message, validate: options[:validate]
|
238
|
-
|
239
|
-
# task.wait return the result of the task. if the task raised an exception
|
240
|
-
# it will be reraised. but that mechanish does not work if multiple values
|
241
|
-
# are returned. so manually raise if first element is an exception
|
242
|
-
result = task.wait
|
243
|
-
raise result.first if result.first.is_a? Exception
|
244
|
-
return message, *result
|
245
|
-
else
|
246
|
-
send_message message, validate: options[:validate]
|
247
|
-
message
|
220
|
+
send_while_collecting message, options do |task|
|
221
|
+
collect_status_updates task, status_list, options[:collect].merge(m_id: m_id)
|
248
222
|
end
|
249
223
|
end
|
250
224
|
|
@@ -289,23 +263,8 @@ module RSMP
|
|
289
263
|
"arg" => command_list,
|
290
264
|
"mId" => m_id
|
291
265
|
})
|
292
|
-
|
293
|
-
|
294
|
-
task = @task.async do |task|
|
295
|
-
collect_options = options[:collect].merge command_list: command_list
|
296
|
-
collect_command_responses task, collect_options, m_id
|
297
|
-
end
|
298
|
-
send_message message, validate: options[:validate]
|
299
|
-
|
300
|
-
# task.wait return the result of the task. if the task raised an exception
|
301
|
-
# it will be reraised. but that mechanish does not work if multiple values
|
302
|
-
# are returned. so manually raise if first element is an exception
|
303
|
-
result = task.wait
|
304
|
-
raise result.first if result.first.is_a? Exception
|
305
|
-
return message, *result
|
306
|
-
else
|
307
|
-
send_message message, validate: options[:validate]
|
308
|
-
message
|
266
|
+
send_while_collecting message, options do |task|
|
267
|
+
collect_command_responses task, command_list, options[:collect].merge(m_id: m_id)
|
309
268
|
end
|
310
269
|
end
|
311
270
|
|
@@ -381,5 +340,37 @@ module RSMP
|
|
381
340
|
@supervisor.notify_error e, options if @supervisor
|
382
341
|
end
|
383
342
|
|
343
|
+
def wait_for_alarm parent_task, options={}
|
344
|
+
matching_alarm = nil
|
345
|
+
message = collect(parent_task,options.merge(type: "Alarm", with_message: true, num: 1)) do |message|
|
346
|
+
# TODO check components
|
347
|
+
matching_alarm = nil
|
348
|
+
alarm = message
|
349
|
+
next if options[:aCId] && options[:aCId] != alarm.attribute("aCId")
|
350
|
+
next if options[:aSp] && options[:aSp] != alarm.attribute("aSp")
|
351
|
+
next if options[:aS] && options[:aS] != alarm.attribute("aS")
|
352
|
+
matching_alarm = alarm
|
353
|
+
break
|
354
|
+
end
|
355
|
+
if item
|
356
|
+
{ message: message, status: matching_alarm }
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def collect_status_updates task, status_list, options
|
361
|
+
StatusUpdateMatcher.new(self, status_list, options).collect task
|
362
|
+
end
|
363
|
+
|
364
|
+
def collect_status_responses task, status_list, options
|
365
|
+
StatusResponseMatcher.new(self, status_list, options).collect task
|
366
|
+
end
|
367
|
+
|
368
|
+
def collect_command_responses task, command_list, options
|
369
|
+
CommandResponseMatcher.new(self, command_list, options).collect task
|
370
|
+
end
|
371
|
+
|
372
|
+
def wait_for_aggregated_status task, options
|
373
|
+
AggregatedStatusMatcher.new(self, options).collect task
|
374
|
+
end
|
384
375
|
end
|
385
376
|
end
|
data/lib/rsmp/site_proxy_wait.rb
CHANGED
@@ -1,184 +0,0 @@
|
|
1
|
-
# waiting for various types of messages and reponses from remote sites
|
2
|
-
module RSMP
|
3
|
-
module SiteProxyWait
|
4
|
-
|
5
|
-
class Matcher
|
6
|
-
attr_reader :result, :messages
|
7
|
-
|
8
|
-
# Initialize with a list a wanted statuses
|
9
|
-
def initialize want, options={}
|
10
|
-
@want = want.clone
|
11
|
-
@result = {}
|
12
|
-
@messages = []
|
13
|
-
@m_id = options[:m_id]
|
14
|
-
end
|
15
|
-
|
16
|
-
# Check if a messages is wanted.
|
17
|
-
# Returns true when we found all that we want.
|
18
|
-
def process message
|
19
|
-
ack_status = check_not_ack message
|
20
|
-
return ack_status if ack_status != nil
|
21
|
-
|
22
|
-
add = false
|
23
|
-
@want.each_with_index do |query,i| # look through wanted
|
24
|
-
get_items(message).each do |input| # look through status items in message
|
25
|
-
matching = match? query, input
|
26
|
-
if matching == true
|
27
|
-
@result[query] = input
|
28
|
-
add = true
|
29
|
-
elsif matching == false
|
30
|
-
@result.delete query
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
@messages << message if add
|
35
|
-
@result.size == @want.size # queries left to match?
|
36
|
-
end
|
37
|
-
|
38
|
-
# Check for MessageNotAck
|
39
|
-
# If the original request identified by @m_id is rejected, we abort
|
40
|
-
def check_not_ack message
|
41
|
-
if message.is_a?(MessageNotAck)
|
42
|
-
if message.attribute('oMId') == @m_id
|
43
|
-
# Set result to an exception, but don't raise it.
|
44
|
-
# This will be returned by the async task and stored as the task result
|
45
|
-
# When the parent task call wait() on the task, the exception
|
46
|
-
# will be raised in the parent task, and caught by RSpec.
|
47
|
-
# RSpec will then show the error and record the test as failed
|
48
|
-
m_id_short = RSMP::Message.shorten_m_id @m_id, 8
|
49
|
-
@result = RSMP::MessageRejected.new("#{type_str} #{m_id_short} was rejected: #{message.attribute('rea')}")
|
50
|
-
@messages = [message]
|
51
|
-
return true
|
52
|
-
end
|
53
|
-
return false
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
class CommandResponseMatcher < RSMP::SiteProxyWait::Matcher
|
59
|
-
def initialize want, options={}
|
60
|
-
super
|
61
|
-
end
|
62
|
-
|
63
|
-
def type_str
|
64
|
-
"Command request"
|
65
|
-
end
|
66
|
-
|
67
|
-
def get_items message
|
68
|
-
message.attributes['rvs']
|
69
|
-
end
|
70
|
-
|
71
|
-
def match? query, item
|
72
|
-
return nil if query['cCI'] && query['cCI'] != item['cCI']
|
73
|
-
return nil if query['n'] && query['n'] != item['n']
|
74
|
-
if query['v'].is_a? Regexp
|
75
|
-
return false if query['v'] && item['v'] !~ query['v']
|
76
|
-
else
|
77
|
-
return false if query['v'] && item['v'] != query['v']
|
78
|
-
end
|
79
|
-
true
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Class for matching incoming messaging against a list of wanted statuses,
|
84
|
-
# and flagging when everything has been matched.
|
85
|
-
class StatusResponseMatcher < RSMP::SiteProxyWait::Matcher
|
86
|
-
def initialize want, options={}
|
87
|
-
super
|
88
|
-
end
|
89
|
-
|
90
|
-
def type_str
|
91
|
-
"Status request"
|
92
|
-
end
|
93
|
-
|
94
|
-
def get_items message
|
95
|
-
message.attributes['sS']
|
96
|
-
end
|
97
|
-
|
98
|
-
# Match an item against a query
|
99
|
-
def match? query, item
|
100
|
-
return nil if query['sCI'] && query['sCI'] != item['sCI']
|
101
|
-
return nil if query['n'] && query['n'] != item['n']
|
102
|
-
return false if query['q'] && query['q'] != item['q']
|
103
|
-
if query['s'].is_a? Regexp
|
104
|
-
return false if query['s'] && item['s'] !~ query['s']
|
105
|
-
else
|
106
|
-
return false if query['s'] && item['s'] != query['s']
|
107
|
-
end
|
108
|
-
true
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def wait_for_alarm parent_task, options={}
|
113
|
-
matching_alarm = nil
|
114
|
-
message = collect(parent_task,options.merge(type: "Alarm", with_message: true, num: 1)) do |message|
|
115
|
-
# TODO check components
|
116
|
-
matching_alarm = nil
|
117
|
-
alarm = message
|
118
|
-
next if options[:aCId] && options[:aCId] != alarm.attribute("aCId")
|
119
|
-
next if options[:aSp] && options[:aSp] != alarm.attribute("aSp")
|
120
|
-
next if options[:aS] && options[:aS] != alarm.attribute("aS")
|
121
|
-
matching_alarm = alarm
|
122
|
-
break
|
123
|
-
end
|
124
|
-
if item
|
125
|
-
{ message: message, status: matching_alarm }
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def collect_status_updates task, options, m_id
|
130
|
-
collect_status_updates_or_responses task, 'StatusUpdate', options, m_id
|
131
|
-
end
|
132
|
-
|
133
|
-
def collect_status_responses task, options, m_id
|
134
|
-
collect_status_updates_or_responses task, 'StatusResponse', options, m_id
|
135
|
-
end
|
136
|
-
|
137
|
-
def collect_command_responses parent_task, options, m_id
|
138
|
-
task.annotate "wait for command response"
|
139
|
-
matcher = CommandResponseMatcher.new options[:command_list], m_id: m_id
|
140
|
-
collect(parent_task,options.merge(type: ['CommandResponse','MessageNotAck'], num: 1)) do |message|
|
141
|
-
matcher.process message # returns true when done (all queries matched)
|
142
|
-
end
|
143
|
-
return matcher.result, matcher.messages
|
144
|
-
rescue Async::TimeoutError
|
145
|
-
raise RSMP::TimeoutError.new "Did not receive correct command response to #{m_id} within #{options[:timeout]}s"
|
146
|
-
end
|
147
|
-
|
148
|
-
def collect_status_updates_or_responses task, type, options, m_id
|
149
|
-
matcher = StatusResponseMatcher.new options[:status_list], m_id: m_id
|
150
|
-
collect(task,options.merge( type: [type,'MessageNotAck'], num: 1 )) do |message|
|
151
|
-
matcher.process message # returns true when done (all queries matched)
|
152
|
-
end
|
153
|
-
return matcher.result, matcher.messages
|
154
|
-
rescue Async::TimeoutError
|
155
|
-
type_str = {'StatusUpdate'=>'update', 'StatusResponse'=>'response'}[type]
|
156
|
-
raise RSMP::TimeoutError.new "Did not received correct status #{type_str} in reply to #{m_id} within #{options[:timeout]}s"
|
157
|
-
end
|
158
|
-
|
159
|
-
def wait_for_aggregated_status parent_task, options, m_id
|
160
|
-
collect(parent_task,options.merge({
|
161
|
-
type: ['AggregatedStatus','MessageNotAck'],
|
162
|
-
num: 1
|
163
|
-
})) do |message|
|
164
|
-
if message.is_a?(MessageNotAck)
|
165
|
-
if message.attribute('oMId') == m_id
|
166
|
-
# set result to an exception, but don't raise it.
|
167
|
-
# this will be returned by the task and stored as the task result
|
168
|
-
# when the parent task call wait() on the task, the exception
|
169
|
-
# will be raised in the parent task, and caught by rspec.
|
170
|
-
# rspec will then show the error and record the test as failed
|
171
|
-
m_id_short = RSMP::Message.shorten_m_id m_id, 8
|
172
|
-
result = RSMP::MessageRejected.new "Aggregated status request #{m_id_short} was rejected: #{message.attribute('rea')}"
|
173
|
-
next true # done, no more messages wanted
|
174
|
-
else
|
175
|
-
false
|
176
|
-
end
|
177
|
-
else
|
178
|
-
true
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
end
|
data/lib/rsmp/supervisor.rb
CHANGED
@@ -225,7 +225,12 @@ module RSMP
|
|
225
225
|
return site if site
|
226
226
|
wait_for(@site_id_condition,timeout) { find_site site_id }
|
227
227
|
rescue Async::TimeoutError
|
228
|
-
|
228
|
+
if site_id == :any
|
229
|
+
str = "No site connected"
|
230
|
+
else
|
231
|
+
str = "Site '#{site_id}' did not connect"
|
232
|
+
end
|
233
|
+
raise RSMP::TimeoutError.new "#{str} within #{timeout}s"
|
229
234
|
end
|
230
235
|
|
231
236
|
def wait_for_site_disconnect site_id, timeout
|
data/lib/rsmp/version.rb
CHANGED
data/lib/rsmp.rb
CHANGED
@@ -21,11 +21,11 @@ require 'rsmp/notifier'
|
|
21
21
|
|
22
22
|
require 'rsmp/listener'
|
23
23
|
require 'rsmp/collector'
|
24
|
+
require 'rsmp/matcher'
|
24
25
|
require 'rsmp/component'
|
25
26
|
require 'rsmp/site'
|
26
27
|
require 'rsmp/proxy'
|
27
28
|
require 'rsmp/supervisor_proxy'
|
28
|
-
require 'rsmp/site_proxy_wait'
|
29
29
|
require 'rsmp/site_proxy'
|
30
30
|
require 'rsmp/error'
|
31
31
|
require 'rsmp/message'
|
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.3.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: 2021-10-
|
11
|
+
date: 2021-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -217,6 +217,7 @@ files:
|
|
217
217
|
- lib/rsmp/listener.rb
|
218
218
|
- lib/rsmp/logger.rb
|
219
219
|
- lib/rsmp/logging.rb
|
220
|
+
- lib/rsmp/matcher.rb
|
220
221
|
- lib/rsmp/message.rb
|
221
222
|
- lib/rsmp/node.rb
|
222
223
|
- lib/rsmp/notifier.rb
|