rsmp 0.3.8 → 0.4.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 +1 -1
- data/lib/rsmp/{collector.rb → collect/collector.rb} +17 -7
- data/lib/rsmp/{listener.rb → collect/listener.rb} +0 -0
- data/lib/rsmp/collect/matcher.rb +109 -0
- data/lib/rsmp/collect/message_matchers.rb +64 -0
- data/lib/rsmp/collect/message_queries.rb +33 -0
- data/lib/rsmp/{notifier.rb → collect/notifier.rb} +0 -0
- data/lib/rsmp/collect/query.rb +32 -0
- data/lib/rsmp/tlc.rb +13 -5
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +7 -4
- metadata +10 -7
- data/lib/rsmp/matcher.rb +0 -195
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ba7b8acadb23f7da783edca08dd93f77ba6183a69d446916a2761945f0e18a2
|
4
|
+
data.tar.gz: 3ac4ae7510f2bb97e2b399e3ae1b4c7d8a8921ae1937c305244d907ff67a1109
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62e4656b7628db4d059a9b3375558016e87fc52bfc9e0d605936adbb4a41d07f963d568f331c9498e7bd730bfa947b16c4b0fe20a881d63fdc13dbbbc70a7b2d
|
7
|
+
data.tar.gz: d54c99af5cd58d20c37715ca757ce946a5e0223a3ab64a892ef98f8ff71cf370d5099a459060a050f2f29f63167cf1770315677674cae1efef6c19bd980bb634
|
data/Gemfile.lock
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module RSMP
|
2
2
|
|
3
|
-
# Collects ingoing and/or outgoing messages.
|
3
|
+
# Collects ingoing and/or outgoing messages from a notifier.
|
4
4
|
# Can filter by message type and wakes up the client once the desired number of messages has been collected.
|
5
5
|
class Collector < Listener
|
6
6
|
attr_reader :condition, :messages, :done
|
@@ -17,22 +17,29 @@ module RSMP
|
|
17
17
|
reset
|
18
18
|
end
|
19
19
|
|
20
|
+
# Inspect formatter that shows the message we have collected
|
20
21
|
def inspect
|
21
22
|
"#<#{self.class.name}:#{self.object_id}, #{inspector(:@messages)}>"
|
22
23
|
end
|
23
24
|
|
25
|
+
# Want ingoing messages?
|
24
26
|
def ingoing?
|
25
27
|
@ingoing == true
|
26
28
|
end
|
27
29
|
|
30
|
+
# Want outgoing messages?
|
28
31
|
def outgoing?
|
29
32
|
@outgoing == true
|
30
33
|
end
|
31
34
|
|
35
|
+
# Block until all messages have been collected
|
32
36
|
def wait
|
33
37
|
@condition.wait
|
34
38
|
end
|
35
39
|
|
40
|
+
# Collect message
|
41
|
+
# Will block until all messages have been collected,
|
42
|
+
# or we time out
|
36
43
|
def collect task, options={}, &block
|
37
44
|
@options.merge! options
|
38
45
|
@block = block
|
@@ -46,17 +53,20 @@ module RSMP
|
|
46
53
|
return @error if @error
|
47
54
|
self
|
48
55
|
rescue Async::TimeoutError
|
49
|
-
str = "
|
56
|
+
str = "#{@title.capitalize} collection"
|
50
57
|
str << " in response to #{options[:m_id]}" if options[:m_id]
|
51
|
-
str << " within #{@options[:timeout]}s"
|
58
|
+
str << " didn't complete within #{@options[:timeout]}s"
|
52
59
|
raise RSMP::TimeoutError.new str
|
53
60
|
end
|
54
61
|
|
62
|
+
# Get the collected message.
|
63
|
+
def message
|
64
|
+
@messages.first
|
65
|
+
end
|
66
|
+
|
55
67
|
# Get the collected messages.
|
56
|
-
|
57
|
-
|
58
|
-
return @messages.first if @options[:num] == 1
|
59
|
-
@messages.first @options[:num]
|
68
|
+
def messages
|
69
|
+
@messages
|
60
70
|
end
|
61
71
|
|
62
72
|
# Clear all query results
|
File without changes
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module RSMP
|
2
|
+
# Base class for waiting for specific status or command responses, specified by
|
3
|
+
# a list of queries. Queries are defined as an array of hashes, e.g
|
4
|
+
# [
|
5
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"securityCode", "v"=>"1111"},
|
6
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"year", "v"=>"2020"},
|
7
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/}
|
8
|
+
# ]
|
9
|
+
#
|
10
|
+
# Note that queries can contain regex patterns for values, like /\d+/ in the example above.
|
11
|
+
#
|
12
|
+
# When an input messages is received it typically contains several items, eg:
|
13
|
+
# [
|
14
|
+
# {"cCI"=>"M0104", "n"=>"month", "v"=>"9", "age"=>"recent"},
|
15
|
+
# {"cCI"=>"M0104", "n"=>"day", "v"=>"29", "age"=>"recent"},
|
16
|
+
# {"cCI"=>"M0104", "n"=>"hour", "v"=>"17", "age"=>"recent"}
|
17
|
+
# ]
|
18
|
+
#
|
19
|
+
# Each input item is matched against each of the queries.
|
20
|
+
# If a match is found, it's stored in the @results hash, with the query as the key,
|
21
|
+
# and a mesage and status as the key. In the example above, this query:
|
22
|
+
#
|
23
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/}
|
24
|
+
#
|
25
|
+
# matches this input:
|
26
|
+
#
|
27
|
+
# {"cCI"=>"M0104", "n"=>"month", "v"=>"9", "age"=>"recent"}
|
28
|
+
#
|
29
|
+
# And the result is stored as:
|
30
|
+
# {
|
31
|
+
# {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/} =>
|
32
|
+
# { <StatusResponse message>, {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>"9"} }
|
33
|
+
# }
|
34
|
+
|
35
|
+
class Matcher < Collector
|
36
|
+
attr_reader :queries
|
37
|
+
|
38
|
+
# Initialize with a list of wanted statuses
|
39
|
+
def initialize proxy, want, options={}
|
40
|
+
super proxy, options.merge( ingoing: true, outgoing: false)
|
41
|
+
@queries = want.map { |item| build_query item }
|
42
|
+
@want = want
|
43
|
+
end
|
44
|
+
|
45
|
+
# Build a query object.
|
46
|
+
# Sub-classes should override to use their own query classes.
|
47
|
+
def build_query want
|
48
|
+
Query.new want
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get a results
|
52
|
+
def query_result want
|
53
|
+
query = @queries.find { |q| q.want == want}
|
54
|
+
raise unless query
|
55
|
+
query.got
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get an array of the last item received for each query
|
59
|
+
def reached
|
60
|
+
@queries.map { |query| query.got }.compact
|
61
|
+
end
|
62
|
+
|
63
|
+
# get the first message. Useful when you only collect one mesage
|
64
|
+
def message
|
65
|
+
@queries.first.message
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get messages from results
|
69
|
+
def messages
|
70
|
+
@queries.map { |query| query.message }.uniq
|
71
|
+
end
|
72
|
+
|
73
|
+
# Are there queries left to match?
|
74
|
+
def done?
|
75
|
+
@queries.all? { |query| query.done? }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Get a simplified hash of queries, with values set to either true or false,
|
79
|
+
# indicating which queries have been matched.
|
80
|
+
def status
|
81
|
+
@queries.map { |query| [query.want, query.done?] }.to_h
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get a simply array of bools, showing which queries ahve been matched.
|
85
|
+
def summary
|
86
|
+
@queries.map { |query| query.done? }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Check if a messages matches our criteria.
|
90
|
+
# We iterate through each of the status items or return values in the message
|
91
|
+
# Breaks as soon as where done matching all queries
|
92
|
+
def check_match message
|
93
|
+
return unless match?(message)
|
94
|
+
@queries.each do |query| # look through queries
|
95
|
+
get_items(message).each do |item| # look through status items in message
|
96
|
+
matched = query.check_match(item,message)
|
97
|
+
if matched != nil
|
98
|
+
type = {true=>'positive',false=>'negative'}[matched]
|
99
|
+
@proxy.log "Query #{query.want} has #{type} match with item #{item}, reached #{summary}", message: message, level: :info
|
100
|
+
break matched
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
# @proxy.log "match", level: :info
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module RSMP
|
2
|
+
# Class for waiting for specific command responses
|
3
|
+
class CommandResponseMatcher < Matcher
|
4
|
+
def initialize proxy, want, options={}
|
5
|
+
super proxy, want, options.merge(
|
6
|
+
type: ['CommandResponse','MessageNotAck'],
|
7
|
+
title:'command response'
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_query want
|
12
|
+
CommandQuery.new want
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get items, in our case the return values
|
16
|
+
def get_items message
|
17
|
+
message.attributes['rvs']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Base class for waiting for status updates or responses
|
22
|
+
class StatusUpdateOrResponseMatcher < Matcher
|
23
|
+
def initialize proxy, want, options={}
|
24
|
+
super proxy, want, options.merge
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_query want
|
28
|
+
StatusQuery.new want
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get items, in our case status values
|
32
|
+
def get_items message
|
33
|
+
message.attributes['sS']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Class for waiting for specific status responses
|
38
|
+
class StatusResponseMatcher < StatusUpdateOrResponseMatcher
|
39
|
+
def initialize proxy, want, options={}
|
40
|
+
super proxy, want, options.merge(
|
41
|
+
type: ['StatusResponse','MessageNotAck'],
|
42
|
+
title: 'status response'
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Class for waiting for specific status responses
|
48
|
+
class StatusUpdateMatcher < StatusUpdateOrResponseMatcher
|
49
|
+
def initialize proxy, want, options={}
|
50
|
+
super proxy, want, options.merge(
|
51
|
+
type: ['StatusUpdate','MessageNotAck'],
|
52
|
+
title:'status update'
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Class for waiting for an aggregated status response
|
58
|
+
class AggregatedStatusMatcher < Collector
|
59
|
+
def initialize proxy, options={}
|
60
|
+
required = { type: ['AggregatedStatus','MessageNotAck'], title: 'aggregated status' }
|
61
|
+
super proxy, options.merge(required)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RSMP
|
2
|
+
# Match a specific command responses
|
3
|
+
class CommandQuery < Query
|
4
|
+
# Match a return value item against a query
|
5
|
+
def match? item
|
6
|
+
return nil if @want['cCI'] && @want['cCI'] != item['cCI']
|
7
|
+
return nil if @want['n'] && @want['n'] != item['n']
|
8
|
+
if @want['v'].is_a? Regexp
|
9
|
+
return false if @want['v'] && item['v'] !~ @want['v']
|
10
|
+
else
|
11
|
+
return false if @want['v'] && item['v'] != @want['v']
|
12
|
+
end
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Match a specific status response or update
|
18
|
+
class StatusQuery < Query
|
19
|
+
# Match a status value against a query
|
20
|
+
def match? item
|
21
|
+
return nil if @want['sCI'] && @want['sCI'] != item['sCI']
|
22
|
+
return nil if @want['cO'] && @want['cO'] != item['cO']
|
23
|
+
return nil if @want['n'] && @want['n'] != item['n']
|
24
|
+
return false if @want['q'] && @want['q'] != item['q']
|
25
|
+
if @want['s'].is_a? Regexp
|
26
|
+
return false if item['s'] !~ @want['s']
|
27
|
+
elsif @want['s']
|
28
|
+
return false if item['s'] != @want['s']
|
29
|
+
end
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
File without changes
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RSMP
|
2
|
+
|
3
|
+
# Class that matches a single status or command item
|
4
|
+
class Query
|
5
|
+
attr_reader :want, :got, :message
|
6
|
+
|
7
|
+
def initialize want
|
8
|
+
@want = want
|
9
|
+
@got = nil
|
10
|
+
@message = nil
|
11
|
+
@done = false
|
12
|
+
end
|
13
|
+
|
14
|
+
# Are we done, i.e. did the last checked item match?
|
15
|
+
def done?
|
16
|
+
@done
|
17
|
+
end
|
18
|
+
|
19
|
+
# Check an item and set @done to true if it matches
|
20
|
+
# Always store the item and corresponding message.
|
21
|
+
def check_match item, message
|
22
|
+
@message = message
|
23
|
+
@got = item
|
24
|
+
matched = match? item
|
25
|
+
@done = matched if matched != nil
|
26
|
+
matched
|
27
|
+
end
|
28
|
+
|
29
|
+
def match? item
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/rsmp/tlc.rb
CHANGED
@@ -293,7 +293,8 @@ module RSMP
|
|
293
293
|
def handle_s0002 status_code, status_name=nil
|
294
294
|
case status_name
|
295
295
|
when 'detectorlogicstatus'
|
296
|
-
RSMP::Tlc.make_status @detector_logics.
|
296
|
+
RSMP::Tlc.make_status @detector_logics.each { |dl| p dl.value }
|
297
|
+
RSMP::Tlc.make_status @detector_logics.map { |dl| dl.value ? '1' : '0' }.join
|
297
298
|
end
|
298
299
|
end
|
299
300
|
|
@@ -703,7 +704,7 @@ module RSMP
|
|
703
704
|
end
|
704
705
|
|
705
706
|
class DetectorLogic < Component
|
706
|
-
attr_reader :
|
707
|
+
attr_reader :forced, :value
|
707
708
|
|
708
709
|
def initialize node:, id:
|
709
710
|
super node: node, id: id, grouped: false
|
@@ -783,13 +784,20 @@ module RSMP
|
|
783
784
|
|
784
785
|
def handle_m0008 arg
|
785
786
|
@node.verify_security_code 2, arg['securityCode']
|
786
|
-
|
787
|
+
status = arg['status']=='True'
|
788
|
+
mode = arg['mode']=='True'
|
789
|
+
force_detector_logic status, mode
|
787
790
|
arg
|
788
791
|
end
|
789
792
|
|
790
|
-
def force_detector_logic
|
791
|
-
@forced =
|
793
|
+
def force_detector_logic forced, value
|
794
|
+
@forced = forced
|
792
795
|
@value = value
|
796
|
+
if @forced
|
797
|
+
log "Forcing to #{value}", level: :info
|
798
|
+
else
|
799
|
+
log "Releasing", level: :info
|
800
|
+
end
|
793
801
|
end
|
794
802
|
|
795
803
|
end
|
data/lib/rsmp/version.rb
CHANGED
data/lib/rsmp.rb
CHANGED
@@ -17,10 +17,13 @@ require 'rsmp/wait'
|
|
17
17
|
require 'rsmp/node'
|
18
18
|
require 'rsmp/supervisor'
|
19
19
|
require 'rsmp/components'
|
20
|
-
require 'rsmp/notifier'
|
21
|
-
require 'rsmp/listener'
|
22
|
-
require 'rsmp/collector'
|
23
|
-
require 'rsmp/
|
20
|
+
require 'rsmp/collect/notifier'
|
21
|
+
require 'rsmp/collect/listener'
|
22
|
+
require 'rsmp/collect/collector'
|
23
|
+
require 'rsmp/collect/query'
|
24
|
+
require 'rsmp/collect/matcher'
|
25
|
+
require 'rsmp/collect/message_queries'
|
26
|
+
require 'rsmp/collect/message_matchers'
|
24
27
|
require 'rsmp/component'
|
25
28
|
require 'rsmp/site'
|
26
29
|
require 'rsmp/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.4.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: 2021-10-
|
11
|
+
date: 2021-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -205,7 +205,13 @@ files:
|
|
205
205
|
- lib/rsmp.rb
|
206
206
|
- lib/rsmp/archive.rb
|
207
207
|
- lib/rsmp/cli.rb
|
208
|
-
- lib/rsmp/collector.rb
|
208
|
+
- lib/rsmp/collect/collector.rb
|
209
|
+
- lib/rsmp/collect/listener.rb
|
210
|
+
- lib/rsmp/collect/matcher.rb
|
211
|
+
- lib/rsmp/collect/message_matchers.rb
|
212
|
+
- lib/rsmp/collect/message_queries.rb
|
213
|
+
- lib/rsmp/collect/notifier.rb
|
214
|
+
- lib/rsmp/collect/query.rb
|
209
215
|
- lib/rsmp/component.rb
|
210
216
|
- lib/rsmp/components.rb
|
211
217
|
- lib/rsmp/convert/export/json_schema.rb
|
@@ -213,13 +219,10 @@ files:
|
|
213
219
|
- lib/rsmp/deep_merge.rb
|
214
220
|
- lib/rsmp/error.rb
|
215
221
|
- lib/rsmp/inspect.rb
|
216
|
-
- lib/rsmp/listener.rb
|
217
222
|
- lib/rsmp/logger.rb
|
218
223
|
- lib/rsmp/logging.rb
|
219
|
-
- lib/rsmp/matcher.rb
|
220
224
|
- lib/rsmp/message.rb
|
221
225
|
- lib/rsmp/node.rb
|
222
|
-
- lib/rsmp/notifier.rb
|
223
226
|
- lib/rsmp/proxy.rb
|
224
227
|
- lib/rsmp/rsmp.rb
|
225
228
|
- lib/rsmp/site.rb
|
@@ -255,7 +258,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
255
258
|
- !ruby/object:Gem::Version
|
256
259
|
version: '0'
|
257
260
|
requirements: []
|
258
|
-
rubygems_version: 3.2.
|
261
|
+
rubygems_version: 3.2.15
|
259
262
|
signing_key:
|
260
263
|
specification_version: 4
|
261
264
|
summary: RoadSide Message Protocol (RSMP) library.
|
data/lib/rsmp/matcher.rb
DELETED
@@ -1,195 +0,0 @@
|
|
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
|
-
attr_reader :queries
|
39
|
-
|
40
|
-
# Initialize with a list a wanted statuses
|
41
|
-
def initialize proxy, want, options={}
|
42
|
-
super proxy, options.merge( ingoing: true, outgoing: false)
|
43
|
-
@queries = {}
|
44
|
-
want.each do |query|
|
45
|
-
@queries[query] = nil
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Get the results, as a hash of queries => results
|
50
|
-
def result
|
51
|
-
@queries
|
52
|
-
end
|
53
|
-
|
54
|
-
# Get messages from results
|
55
|
-
def messages
|
56
|
-
@queries.map { |query,result| result[:message] }.uniq
|
57
|
-
end
|
58
|
-
|
59
|
-
# Get items from results
|
60
|
-
def items
|
61
|
-
@queries.map { |query,result| result[:item] }.uniq
|
62
|
-
end
|
63
|
-
|
64
|
-
# Are there queries left to match?
|
65
|
-
def done?
|
66
|
-
@queries.values.all? { |result| result != nil }
|
67
|
-
end
|
68
|
-
|
69
|
-
# Get a simplified hash of queries, with values set to either true or false,
|
70
|
-
# indicating which queries have been matched.
|
71
|
-
def status
|
72
|
-
@queries.transform_values{ |v| v != nil }
|
73
|
-
end
|
74
|
-
|
75
|
-
# Get a simply array of bools, showing which queries ahve been matched.
|
76
|
-
def summary
|
77
|
-
@queries.values.map { |v| v != nil }
|
78
|
-
end
|
79
|
-
|
80
|
-
# Mark a query as matched, by linking it to the matched item and message
|
81
|
-
def keep query, message, item
|
82
|
-
@queries[query] = { message:message, item:item }
|
83
|
-
end
|
84
|
-
|
85
|
-
# Mark a query as not matched
|
86
|
-
def forget query
|
87
|
-
@queries[query] = nil
|
88
|
-
end
|
89
|
-
|
90
|
-
# Check if a messages matches our criteria.
|
91
|
-
# We iterate through each of the status items or return values in the message
|
92
|
-
# Breaks as soon as where done matching all queries
|
93
|
-
def check_match message
|
94
|
-
return unless match?(message)
|
95
|
-
@queries.keys.each do |query| # look through queries
|
96
|
-
get_items(message).each do |item| # look through status items in message
|
97
|
-
break if check_item_match message, query, item
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# Check if an item matches, and mark query as matched/unmatched accordingly.
|
103
|
-
def check_item_match message, query, item
|
104
|
-
matched = match_item? query, item
|
105
|
-
if matched == true
|
106
|
-
keep query, message, item
|
107
|
-
true
|
108
|
-
elsif matched == false
|
109
|
-
forget query
|
110
|
-
true
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Class for waiting for specific command responses
|
116
|
-
class CommandResponseMatcher < Matcher
|
117
|
-
def initialize proxy, want, options={}
|
118
|
-
super proxy, want, options.merge(
|
119
|
-
type: ['CommandResponse','MessageNotAck'],
|
120
|
-
title:'command response'
|
121
|
-
)
|
122
|
-
end
|
123
|
-
|
124
|
-
# Get items, in our case the return values
|
125
|
-
def get_items message
|
126
|
-
message.attributes['rvs']
|
127
|
-
end
|
128
|
-
|
129
|
-
# Match a return value item against a query
|
130
|
-
def match_item? query, item
|
131
|
-
return nil if query['cCI'] && query['cCI'] != item['cCI']
|
132
|
-
return nil if query['n'] && query['n'] != item['n']
|
133
|
-
if query['v'].is_a? Regexp
|
134
|
-
return false if query['v'] && item['v'] !~ query['v']
|
135
|
-
else
|
136
|
-
return false if query['v'] && item['v'] != query['v']
|
137
|
-
end
|
138
|
-
true
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
# Base class for waiting for status updates or responses
|
143
|
-
class StatusUpdateOrResponseMatcher < Matcher
|
144
|
-
def initialize proxy, want, options={}
|
145
|
-
super proxy, want, options.merge
|
146
|
-
end
|
147
|
-
|
148
|
-
# Get items, in our case status values
|
149
|
-
def get_items message
|
150
|
-
message.attributes['sS']
|
151
|
-
end
|
152
|
-
|
153
|
-
# Match a status value against a query
|
154
|
-
def match_item? query, item
|
155
|
-
return nil if query['sCI'] && query['sCI'] != item['sCI']
|
156
|
-
return nil if query['cO'] && query['cO'] != item['cO']
|
157
|
-
return nil if query['n'] && query['n'] != item['n']
|
158
|
-
return false if query['q'] && query['q'] != item['q']
|
159
|
-
if query['s'].is_a? Regexp
|
160
|
-
return false if query['s'] && item['s'] !~ query['s']
|
161
|
-
else
|
162
|
-
return false if query['s'] && item['s'] != query['s']
|
163
|
-
end
|
164
|
-
true
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# Class for waiting for specific status responses
|
169
|
-
class StatusResponseMatcher < StatusUpdateOrResponseMatcher
|
170
|
-
def initialize proxy, want, options={}
|
171
|
-
super proxy, want, options.merge(
|
172
|
-
type: ['StatusResponse','MessageNotAck'],
|
173
|
-
title: 'status response'
|
174
|
-
)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
# Class for waiting for specific status responses
|
179
|
-
class StatusUpdateMatcher < StatusUpdateOrResponseMatcher
|
180
|
-
def initialize proxy, want, options={}
|
181
|
-
super proxy, want, options.merge(
|
182
|
-
type: ['StatusUpdate','MessageNotAck'],
|
183
|
-
title:'status update'
|
184
|
-
)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
# Class for waiting for an aggregated status response
|
189
|
-
class AggregatedStatusMatcher < Collector
|
190
|
-
def initialize proxy, options={}
|
191
|
-
required = { type: ['AggregatedStatus','MessageNotAck'], title: 'aggregated status' }
|
192
|
-
super proxy, options.merge(required)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|