rsmp 0.7.2 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a5a515a9a42c794ebc27efd12ab9d87a1b0281544e06050bfe63f433bdf4a80
4
- data.tar.gz: 51e2cca7f3e44cf5c4fe6447151436474f1d5001715e8a4ce54c070ce867565e
3
+ metadata.gz: 9614e6db6381449fa893901766de8239b7bc025c4454724709dc32b75551b5f3
4
+ data.tar.gz: fce1a7fb067481f5ca199301bbd91953d0dd8d13f07e66ad365a5ffa24e58966
5
5
  SHA512:
6
- metadata.gz: ac5c99fb01e74f49a7bab931fefe9314c1d4279e7c0be9d688de622c60892edb0138ac72b676261fc6e86681e01e30f7b59d28ee33e853389af509b9229f5982
7
- data.tar.gz: 8050ccd64dc78c49704f403fea069ab62c6f68778a3b3f2fdabba359d74620027959f8ab2bc5ce0fc22193a403301d7454a186fe01f14fd54800534ac640e541
6
+ metadata.gz: b942ee93f8185051dadac06576087ca8b27b11f8e3c653cb9638a72464941dd72c82c566ede50a46d2bffeb16c809345ef78b3edef7174a74d8ea75e963fa8f1
7
+ data.tar.gz: 0052cb6afefed493e4ac4e2d48f4c587afe3f377ddabd2492fbeb3c223ff78e39b23ac7b01cd5e564abc3474a5f8f0b35b6edc93c24c453876597c478365ac2a
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.7.2)
4
+ rsmp (0.8.0)
5
5
  async (~> 1.29.1)
6
6
  async-io (~> 1.32.1)
7
7
  colorize (~> 0.8.1)
@@ -34,7 +34,7 @@ GEM
34
34
  childprocess (4.1.0)
35
35
  colorize (0.8.1)
36
36
  concurrent-ruby (1.1.9)
37
- console (1.13.1)
37
+ console (1.14.0)
38
38
  fiber-local
39
39
  contracts (0.17)
40
40
  cucumber (6.1.0)
@@ -95,7 +95,7 @@ GEM
95
95
  thor
96
96
  thread_safe
97
97
  rake (13.0.3)
98
- regexp_parser (2.1.1)
98
+ regexp_parser (2.2.0)
99
99
  rsmp_schemer (0.3.2)
100
100
  json_schemer (~> 0.2.18)
101
101
  rspec (3.10.0)
@@ -124,6 +124,7 @@ GEM
124
124
 
125
125
  PLATFORMS
126
126
  x86_64-darwin-20
127
+ x86_64-darwin-21
127
128
 
128
129
  DEPENDENCIES
129
130
  aruba (~> 1.1.2)
@@ -136,4 +137,4 @@ DEPENDENCIES
136
137
  timecop (~> 0.9.4)
137
138
 
138
139
  BUNDLED WITH
139
- 2.2.21
140
+ 2.2.32
data/lib/rsmp/cli.rb CHANGED
@@ -3,6 +3,10 @@ require 'rsmp'
3
3
 
4
4
  module RSMP
5
5
  class CLI < Thor
6
+ desc "version", "Show version"
7
+ def version
8
+ puts RSMP::VERSION
9
+ end
6
10
 
7
11
  desc "site", "Run RSMP site"
8
12
  method_option :config, :type => :string, :aliases => "-c", banner: 'Path to .yaml config file'
@@ -3,7 +3,7 @@ module RSMP
3
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
- attr_reader :condition, :messages, :done
6
+ attr_reader :condition, :messages, :status, :error
7
7
 
8
8
  def initialize proxy, options={}
9
9
  super proxy, options
@@ -17,11 +17,17 @@ module RSMP
17
17
  @outgoing = options[:outgoing] == nil ? false : options[:outgoing]
18
18
  @condition = Async::Notification.new
19
19
  @title = options[:title] || [@options[:type]].flatten.join('/')
20
- @options[:timeout] ||= 1
21
- @options[:num] ||= 1
22
20
  reset
23
21
  end
24
22
 
23
+ # Clear all query results
24
+ def reset
25
+ @messages = []
26
+ @error = nil
27
+ @status = :ready
28
+ @why = nil
29
+ end
30
+
25
31
  # Inspect formatter that shows the message we have collected
26
32
  def inspect
27
33
  "#<#{self.class.name}:#{self.object_id}, #{inspector(:@messages)}>"
@@ -37,90 +43,108 @@ module RSMP
37
43
  @outgoing == true
38
44
  end
39
45
 
40
- # Block until all messages have been collected
41
- def wait
42
- @condition.wait
46
+ # If collection is complete, return immeditatly. Otherwise wait until
47
+ # the desired messages have been collected, or timeout is reached.
48
+ def wait task
49
+ wait! task
50
+ rescue RSMP::TimeoutError
51
+ @status
43
52
  end
44
53
 
45
- # Collect message
46
- # Will block until all messages have been collected,
47
- # or we time out
48
- def collect task, options={}, &block
49
- @options.merge! options
50
- @block = block
51
- unless @done
52
- listen do
53
- task.with_timeout(@options[:timeout]) do
54
- @condition.wait
55
- end
56
- end
54
+ # If collection is complete, return immeditatly. Otherwise wait until
55
+ # the desired messages have been collected.
56
+ # If timeout is reached, an exceptioin is raised.
57
+ def wait! task
58
+ return @status unless @status == :collecting
59
+ if @options[:timeout]
60
+ task.with_timeout(@options[:timeout]) { @condition.wait }
61
+ else
62
+ @condition.wait
57
63
  end
58
- return @error if @error
59
- self
64
+ @status
60
65
  rescue Async::TimeoutError
61
- str = "#{@title.capitalize} collection"
62
- str << " in response to #{options[:m_id]}" if options[:m_id]
63
- str << " didn't complete within #{@options[:timeout]}s"
64
- reached = progress
65
- str << ", reached #{progress[:reached]}/#{progress[:need]}"
66
- raise RSMP::TimeoutError.new str
66
+ @status = :timeout
67
+ raise RSMP::TimeoutError.new(describe_progress)
67
68
  end
68
69
 
69
- # Return progress as collected vs. number requested
70
- def progress
71
- need = @options[:num]
72
- reached = @messages.size
73
- { need: need, got: reached }
70
+ # Start collection and return immediately
71
+ # You can later use wait() to wait for completion
72
+ def start options={}, &block
73
+ raise RuntimeError.new("Can't begin unless ready (currenty #{@status})") unless @status == :ready
74
+ @options.merge! options
75
+ @block = block
76
+ raise ArgumentError.new("Num, timeout or block must be provided") unless @options[:num] || @options[:timeout] || @block
77
+ reset
78
+ @status = :collecting
79
+ @notifier.add_listener self if @notifier
74
80
  end
75
81
 
76
- # Get the collected message.
77
- def message
78
- @messages.first
82
+ # Collect message
83
+ # Will return once all messages have been collected, or timeout is reached
84
+ def collect task, options={}, &block
85
+ start options, &block
86
+ wait task
87
+ @status
88
+ ensure
89
+ @notifier.remove_listener self
79
90
  end
80
91
 
81
- # Get the collected messages.
82
- def messages
83
- @messages
92
+ # Build a string describing how how progress reached before timeout
93
+ def describe_progress
94
+ str = "#{@title.capitalize} collection "
95
+ str << "in response to #{@options[:m_id]} " if @options[:m_id]
96
+ str << "didn't complete within #{@options[:timeout]}s, "
97
+ str << "reached #{@messages.size}/#{@options[:num]}"
98
+ str
84
99
  end
85
100
 
86
- # Clear all query results
87
- def reset
88
- @messages = []
89
- @error = nil
90
- @done = false
101
+ # Collect message
102
+ # Returns the collected messages, or raise an exception in case of a time out.
103
+ def collect! task, options={}, &block
104
+ case collect(task, options, &block)
105
+ when :timeout
106
+ raise RSMP::TimeoutError.new @why
107
+ else
108
+ @messages
109
+ end
91
110
  end
92
111
 
93
112
  # Check if we receive a NotAck related to initiating request, identified by @m_id.
94
- def check_not_ack message
113
+ def reject_not_ack message
95
114
  return unless @options[:m_id]
96
115
  if message.is_a?(MessageNotAck)
97
116
  if message.attribute('oMId') == @options[:m_id]
98
117
  m_id_short = RSMP::Message.shorten_m_id @options[:m_id], 8
99
- @error = RSMP::MessageRejected.new("#{@title} #{m_id_short} was rejected with '#{message.attribute('rea')}'")
100
- complete
118
+ cancel RSMP::MessageRejected.new("#{@title} #{m_id_short} was rejected with '#{message.attribute('rea')}'")
119
+ true
101
120
  end
102
- false
103
121
  end
104
122
  end
105
123
 
106
124
  # Handle message. and return true when we're done collecting
107
125
  def notify message
108
126
  raise ArgumentError unless message
109
- raise RuntimeError.new("can't process message when already done") if @done
110
- check_not_ack(message)
111
- return true if @done
112
- check_match message
113
- complete if done?
114
- @done
127
+ raise RuntimeError.new("can't process message when done") unless @status == :ready || @status == :collecting
128
+ unless reject_not_ack(message)
129
+ perform_match message
130
+ end
131
+ @status
115
132
  end
116
133
 
117
134
  # Match message against our collection criteria
118
- def check_match message
119
- matched = match? message
120
- if matched == true
135
+ def perform_match message
136
+ return unless type_match?(message)
137
+ if @block
138
+ status = [@block.call(message)].flatten
139
+ keep message if status.include?(:keep)
140
+ if status.include?(:cancel)
141
+ cancel('Cancelled by block')
142
+ else
143
+ complete if done?
144
+ end
145
+ else
121
146
  keep message
122
- elsif matched == false
123
- forget message
147
+ complete if done?
124
148
  end
125
149
  end
126
150
 
@@ -132,8 +156,14 @@ module RSMP
132
156
  # Called when we're done collecting. Remove ourself as a listener,
133
157
  # se we don't receive message notifications anymore
134
158
  def complete
135
- @done = true
136
- @proxy.remove_listener self
159
+ @status = :ok
160
+ do_stop
161
+ end
162
+
163
+ # Remove ourself as a listener, so we don't receive message notifications anymore,
164
+ # and wake up the async condition
165
+ def do_stop
166
+ @notifier.remove_listener self
137
167
  @condition.signal
138
168
  end
139
169
 
@@ -143,7 +173,7 @@ module RSMP
143
173
  case error
144
174
  when RSMP::SchemaError
145
175
  notify_schema_error error, options
146
- when RSMP::ConnectionError
176
+ when RSMP::DisconnectError
147
177
  notify_disconnect error, options
148
178
  end
149
179
  end
@@ -154,24 +184,23 @@ module RSMP
154
184
  message = options[:message]
155
185
  return unless message
156
186
  klass = message.class.name.split('::').last
157
- return unless [@options[:type]].flatten.include? klass
158
- @proxy.log "Collect cancelled due to schema error in #{klass} #{message.m_id_short}", level: :debug
187
+ return unless @options[:type] == nil || [@options[:type]].flatten.include?(klass)
188
+ @notifier.log "Collection cancelled due to schema error in #{klass} #{message.m_id_short}", level: :debug
159
189
  cancel error
160
190
  end
161
191
 
162
192
  # Cancel if we received e notificaiton about a disconnect
163
193
  def notify_disconnect error, options
164
194
  return unless @options.dig(:cancel,:disconnect)
165
- @proxy.log "Collect cancelled due to a connection error: #{error.to_s}", level: :debug
195
+ @notifier.log "Collection cancelled due to a connection error: #{error.to_s}", level: :debug
166
196
  cancel error
167
197
  end
168
198
 
169
199
  # Abort collection
170
200
  def cancel error
171
- @error = error if error
172
- @done = false
173
- @proxy.remove_listener self
174
- @condition.signal
201
+ @error = error
202
+ @status = :cancelled
203
+ do_stop
175
204
  end
176
205
 
177
206
  # Store a message in the result array
@@ -179,30 +208,20 @@ module RSMP
179
208
  @messages << message
180
209
  end
181
210
 
182
- # Remove a message from the result array
183
- def forget message
184
- @messages.delete message
185
- end
186
-
187
211
  # Check a message against our match criteria
188
- # Return true if there's a match
189
- def match? message
190
- raise ArgumentError unless message
191
- return if message.direction == :in && @ingoing == false
192
- return if message.direction == :out && @outgoing == false
212
+ # Return true if there's a match, false if not
213
+ def type_match? message
214
+ return false if message.direction == :in && @ingoing == false
215
+ return false if message.direction == :out && @outgoing == false
193
216
  if @options[:type]
194
- return if message == nil
195
217
  if @options[:type].is_a? Array
196
- return unless @options[:type].include? message.type
218
+ return false unless @options[:type].include? message.type
197
219
  else
198
- return unless message.type == @options[:type]
220
+ return false unless message.type == @options[:type]
199
221
  end
200
222
  end
201
223
  if @options[:component]
202
- return if message.attributes['cId'] && message.attributes['cId'] != @options[:component]
203
- end
204
- if @block
205
- return if @block.call(message) == false
224
+ return false if message.attributes['cId'] && message.attributes['cId'] != @options[:component]
206
225
  end
207
226
  true
208
227
  end
@@ -5,8 +5,13 @@ module RSMP
5
5
  class Listener
6
6
  include Inspect
7
7
 
8
- def initialize proxy, options={}
9
- @proxy = proxy
8
+ def initialize notifier, options={}
9
+ @notifier = notifier
10
+ end
11
+
12
+ def change_notifier notifier
13
+ @notifier.remove_listener self if @notifier
14
+ @notifier = notifier
10
15
  end
11
16
 
12
17
  def notify message
@@ -16,10 +21,10 @@ module RSMP
16
21
  end
17
22
 
18
23
  def listen &block
19
- @proxy.add_listener self
24
+ @notifier.add_listener self
20
25
  yield
21
26
  ensure
22
- @proxy.remove_listener self
27
+ @notifier.remove_listener self
23
28
  end
24
29
 
25
30
  end
@@ -37,9 +37,9 @@ module RSMP
37
37
 
38
38
  # Initialize with a list of wanted statuses
39
39
  def initialize proxy, want, options={}
40
+ raise ArgumentError.new("num option cannot be used") if options[:num]
40
41
  super proxy, options.merge( ingoing: true, outgoing: false)
41
42
  @queries = want.map { |item| build_query item }
42
- @want = want
43
43
  end
44
44
 
45
45
  # Build a query object.
@@ -60,11 +60,6 @@ module RSMP
60
60
  @queries.map { |query| query.got }.compact
61
61
  end
62
62
 
63
- # get the first message. Useful when you only collect one mesage
64
- def message
65
- @queries.first.message
66
- end
67
-
68
63
  # Get messages from results
69
64
  def messages
70
65
  @queries.map { |query| query.message }.uniq
@@ -77,14 +72,14 @@ module RSMP
77
72
  { need: need, reached: reached }
78
73
  end
79
74
 
80
- # Are there queries left to match?
75
+ # Are there queries left to type_match?
81
76
  def done?
82
77
  @queries.all? { |query| query.done? }
83
78
  end
84
79
 
85
80
  # Get a simplified hash of queries, with values set to either true or false,
86
81
  # indicating which queries have been matched.
87
- def status
82
+ def query_status
88
83
  @queries.map { |query| [query.want, query.done?] }.to_h
89
84
  end
90
85
 
@@ -95,19 +90,23 @@ module RSMP
95
90
 
96
91
  # Check if a messages matches our criteria.
97
92
  # Match each query against each item in the message
98
- def check_match message
99
- return unless match?(message)
93
+ def perform_match message
94
+ return unless type_match?(message)
100
95
  @queries.each do |query| # look through queries
101
96
  get_items(message).each do |item| # look through items in message
102
- matched = query.check_match(item,message)
97
+ matched = query.perform_match(item,message)
98
+ if matched == true
99
+ matched = @block.call(message,item) if @block
100
+ end
103
101
  if matched != nil
104
102
  type = {true=>'match',false=>'mismatch'}[matched]
105
- @proxy.log "#{@title.capitalize} #{message.m_id_short} collect #{type} #{query.want}, item #{item}", level: :debug
103
+ @notifier.log "#{@title.capitalize} #{message.m_id_short} collect #{type} #{query.want}, item #{item}", level: :debug
106
104
  break
107
105
  end
108
106
  end
109
107
  end
110
- @proxy.log "#{@title.capitalize} collect reached #{summary}", level: :debug
108
+ complete if done?
109
+ @notifier.log "#{@title.capitalize} collect reached #{summary}", level: :debug
111
110
  end
112
111
  end
113
112
  end
@@ -0,0 +1,209 @@
1
+ module RSMP
2
+
3
+ # Collects ingoing and/or outgoing messages from a notifier.
4
+ # Can filter by message type and wakes up the client once the desired number of messages has been collected.
5
+ class MessageCollector < Collector
6
+ attr_reader :condition, :messages, :done
7
+
8
+ def initialize proxy, options={}
9
+ super proxy, options
10
+ @options = {
11
+ cancel: {
12
+ schema_error: true,
13
+ disconnect: false,
14
+ }
15
+ }.deep_merge options
16
+ @ingoing = options[:ingoing] == nil ? true : options[:ingoing]
17
+ @outgoing = options[:outgoing] == nil ? false : options[:outgoing]
18
+ @condition = Async::Notification.new
19
+ @title = options[:title] || [@options[:type]].flatten.join('/')
20
+ @options[:timeout] ||= 1
21
+ @options[:num] ||= 1
22
+ reset
23
+ end
24
+
25
+ # Inspect formatter that shows the message we have collected
26
+ def inspect
27
+ "#<#{self.class.name}:#{self.object_id}, #{inspector(:@messages)}>"
28
+ end
29
+
30
+ # Want ingoing messages?
31
+ def ingoing?
32
+ @ingoing == true
33
+ end
34
+
35
+ # Want outgoing messages?
36
+ def outgoing?
37
+ @outgoing == true
38
+ end
39
+
40
+ # Block until all messages have been collected
41
+ def wait
42
+ @condition.wait
43
+ end
44
+
45
+ # Collect message
46
+ # Will block until all messages have been collected,
47
+ # or we time out
48
+ def collect task, options={}, &block
49
+ @options.merge! options
50
+ @block = block
51
+ unless @done
52
+ listen do
53
+ task.with_timeout(@options[:timeout]) do
54
+ @condition.wait
55
+ end
56
+ end
57
+ end
58
+ return @error if @error
59
+ self
60
+ rescue Async::TimeoutError
61
+ str = "#{@title.capitalize} collection"
62
+ str << " in response to #{options[:m_id]}" if options[:m_id]
63
+ str << " didn't complete within #{@options[:timeout]}s"
64
+ reached = progress
65
+ str << ", reached #{progress[:reached]}/#{progress[:need]}"
66
+ raise RSMP::TimeoutError.new str
67
+ end
68
+
69
+ # Return progress as collected vs. number requested
70
+ def progress
71
+ need = @options[:num]
72
+ reached = @messages.size
73
+ { need: need, got: reached }
74
+ end
75
+
76
+ # Get the collected message.
77
+ def message
78
+ @messages.first
79
+ end
80
+
81
+ # Get the collected messages.
82
+ def messages
83
+ @messages
84
+ end
85
+
86
+ # Clear all query results
87
+ def reset
88
+ @messages = []
89
+ @error = nil
90
+ @done = false
91
+ end
92
+
93
+ # Check if we receive a NotAck related to initiating request, identified by @m_id.
94
+ def check_not_ack message
95
+ return unless @options[:m_id]
96
+ if message.is_a?(MessageNotAck)
97
+ if message.attribute('oMId') == @options[:m_id]
98
+ m_id_short = RSMP::Message.shorten_m_id @options[:m_id], 8
99
+ @error = RSMP::MessageRejected.new("#{@title} #{m_id_short} was rejected with '#{message.attribute('rea')}'")
100
+ complete
101
+ end
102
+ false
103
+ end
104
+ end
105
+
106
+ # Handle message. and return true when we're done collecting
107
+ def notify message
108
+ raise ArgumentError unless message
109
+ raise RuntimeError.new("can't process message when already done") if @done
110
+ check_not_ack(message)
111
+ return true if @done
112
+ perform_match message
113
+ complete if done?
114
+ @done
115
+ end
116
+
117
+ # Match message against our collection criteria
118
+ def perform_match message
119
+ matched = type_match?(message) && block_match?(message)
120
+ if matched == true
121
+ keep message
122
+ elsif matched == false
123
+ forget message
124
+ end
125
+ end
126
+
127
+ # Have we collected the required number of messages?
128
+ def done?
129
+ @options[:num] && @messages.size >= @options[:num]
130
+ end
131
+
132
+ # Called when we're done collecting. Remove ourself as a listener,
133
+ # se we don't receive message notifications anymore
134
+ def complete
135
+ @done = true
136
+ @proxy.remove_listener self
137
+ @condition.signal
138
+ end
139
+
140
+ # The proxy experienced some error.
141
+ # Check if this should cause us to cancel.
142
+ def notify_error error, options={}
143
+ case error
144
+ when RSMP::SchemaError
145
+ notify_schema_error error, options
146
+ when RSMP::ConnectionError
147
+ notify_disconnect error, options
148
+ end
149
+ end
150
+
151
+ # Cancel if we received e schema error for a message type we're collecting
152
+ def notify_schema_error error, options
153
+ return unless @options.dig(:cancel,:schema_error)
154
+ message = options[:message]
155
+ return unless message
156
+ klass = message.class.name.split('::').last
157
+ return unless [@options[:type]].flatten.include? klass
158
+ @proxy.log "Collect cancelled due to schema error in #{klass} #{message.m_id_short}", level: :debug
159
+ cancel error
160
+ end
161
+
162
+ # Cancel if we received e notificaiton about a disconnect
163
+ def notify_disconnect error, options
164
+ return unless @options.dig(:cancel,:disconnect)
165
+ @proxy.log "Collect cancelled due to a connection error: #{error.to_s}", level: :debug
166
+ cancel error
167
+ end
168
+
169
+ # Abort collection
170
+ def cancel error
171
+ @error = error if error
172
+ @done = false
173
+ @proxy.remove_listener self
174
+ @condition.signal
175
+ end
176
+
177
+ # Store a message in the result array
178
+ def keep message
179
+ @messages << message
180
+ end
181
+
182
+ # Remove a message from the result array
183
+ def forget message
184
+ @messages.delete message
185
+ end
186
+
187
+ # Check a message against our match criteria
188
+ # Return true if there's a match, false if not
189
+ def type_match? message
190
+ return false if message.direction == :in && @ingoing == false
191
+ return false if message.direction == :out && @outgoing == false
192
+ if @options[:type]
193
+ if @options[:type].is_a? Array
194
+ return false unless @options[:type].include? message.type
195
+ else
196
+ return false unless message.type == @options[:type]
197
+ end
198
+ end
199
+ if @options[:component]
200
+ return false if message.attributes['cId'] && message.attributes['cId'] != @options[:component]
201
+ end
202
+ true
203
+ end
204
+ end
205
+
206
+ def block_match? message
207
+ @block.call(message) == true
208
+ end
209
+ end
@@ -14,7 +14,7 @@ module RSMP
14
14
  end
15
15
  end
16
16
 
17
- # Match a specific status response or update
17
+ # Match a specific status response or update
18
18
  class StatusQuery < Query
19
19
  # Match a status value against a query
20
20
  def match? item
@@ -1,5 +1,5 @@
1
1
  module RSMP
2
-
2
+
3
3
  # Class that matches a single status or command item
4
4
  class Query
5
5
  attr_reader :want, :got, :message
@@ -18,7 +18,7 @@ module RSMP
18
18
 
19
19
  # Check an item and set @done to true if it matches
20
20
  # Store the item and corresponding message if there's a positive or negative match
21
- def check_match item, message
21
+ def perform_match item, message
22
22
  matched = match? item
23
23
  if matched != nil
24
24
  @message = message
data/lib/rsmp/error.rb CHANGED
@@ -41,7 +41,7 @@ module RSMP
41
41
  class TimeoutError < Error
42
42
  end
43
43
 
44
- class DisonnectError < Error
44
+ class DisconnectError < Error
45
45
  end
46
46
 
47
47
  class ConnectionError < Error
data/lib/rsmp/logger.rb CHANGED
@@ -50,7 +50,7 @@ module RSMP
50
50
  'statuses' => ['StatusRequest','StatusSubscribe','StatusUnsubscribe','StatusResponse','StatusUpdate'],
51
51
  'commands' => ['CommandRequest','CommandResponse'],
52
52
  'watchdogs' => 'Watchdog',
53
- 'alarms' => ['AlarmIssue','AlarmRequest','AlarmAcknowledged','AlarmSuspend','AlarmResume'],
53
+ 'alarms' => ['Alarm'],
54
54
  'aggregated_status' => ['AggregatedStatus','AggregatedStatusRequest']
55
55
  }
56
56
 
@@ -105,12 +105,10 @@ module RSMP
105
105
 
106
106
  if item[:message]
107
107
  type = item[:message].type
108
- ack = type == "MessageAck" || type == "MessageNotAck"
108
+ ack = (type == "MessageAck" || type == "MessageNotAck")
109
109
  @ignorable.each_pair do |key,types|
110
- next unless types
111
110
  ignore = [types].flatten
112
111
  if @settings[key] == false
113
- #p [type,ignore_type, [ignore_type].flatten.include?(type)]
114
112
  return false if ignore.include?(type)
115
113
  if ack
116
114
  return false if item[:message].original && ignore.include?(item[:message].original.type)
data/lib/rsmp/node.rb CHANGED
@@ -6,7 +6,7 @@ module RSMP
6
6
  include Wait
7
7
  include Inspect
8
8
 
9
- attr_reader :archive, :logger, :task, :deferred, :error_queue, :clock
9
+ attr_reader :archive, :logger, :task, :deferred, :error_queue, :clock, :collector
10
10
 
11
11
  def initialize options
12
12
  initialize_logging options
@@ -15,6 +15,8 @@ module RSMP
15
15
  @clock = Clock.new
16
16
  @error_queue = Async::Queue.new
17
17
  @ignore_errors = []
18
+ options[:collector]
19
+ @collect = options[:collect]
18
20
  end
19
21
 
20
22
  def ignore_errors classes, &block
data/lib/rsmp/proxy.rb CHANGED
@@ -15,9 +15,8 @@ module RSMP
15
15
 
16
16
  def initialize options
17
17
  initialize_logging options
18
- setup options
19
18
  initialize_distributor
20
- prepare_collection @settings['collect']
19
+ setup options
21
20
  clear
22
21
  end
23
22
 
@@ -37,6 +36,10 @@ module RSMP
37
36
  @sxl = nil
38
37
  @site_settings = nil # can't pick until we know the site id
39
38
  @state = :stopped
39
+ if options[:collect]
40
+ @collector = RSMP::Collector.new self, options[:collect]
41
+ @collector.start
42
+ end
40
43
  end
41
44
 
42
45
  def inspect
@@ -49,13 +52,6 @@ module RSMP
49
52
  node.clock
50
53
  end
51
54
 
52
- def prepare_collection num
53
- if num
54
- @collector = RSMP::Collector.new self, num: num, ingoing: true, outgoing: true
55
- add_listener @collector
56
- end
57
- end
58
-
59
55
  def collect task, options, &block
60
56
  collector = RSMP::Collector.new self, options
61
57
  collector.collect task, &block
@@ -86,7 +82,7 @@ module RSMP
86
82
  return if @state == :stopped
87
83
  set_state :stopping
88
84
  stop_tasks
89
- notify_error DisonnectError.new("Connection was closed")
85
+ notify_error DisconnectError.new("Connection was closed")
90
86
  ensure
91
87
  close_socket
92
88
  clear
@@ -101,15 +101,8 @@ module RSMP
101
101
  "cId" => component,
102
102
  "mId" => m_id
103
103
  })
104
- if options[:collect]
105
- task = @task.async do |task|
106
- collect_aggregated_status task, options[:collect].merge(m_id: m_id)
107
- end
108
- send_message message, validate: options[:validate]
109
- return message, task.wait
110
- else
111
- send_message message, validate: options[:validate]
112
- message
104
+ send_and_collect_if_needed message, options do |task|
105
+ collect_aggregated_status task, options[:collect].merge(m_id: m_id, num:1)
113
106
  end
114
107
  end
115
108
 
@@ -181,7 +174,7 @@ module RSMP
181
174
  "sS" => request_list,
182
175
  "mId" => m_id
183
176
  })
184
- send_while_collecting message, options do |task|
177
+ send_and_collect_if_needed message, options do |task|
185
178
  collect_status_responses task, status_list, options[:collect].merge(m_id: m_id)
186
179
  end
187
180
  end
@@ -193,11 +186,15 @@ module RSMP
193
186
  acknowledge message
194
187
  end
195
188
 
196
- def send_while_collecting message, options, &block
197
- task = @task.async { |task| yield task } if options[:collect]
198
- send_message message, validate: options[:validate]
199
- return message, task.wait if task
200
- message
189
+ def send_and_collect_if_needed message, options, &block
190
+ if options[:collect]
191
+ task = @task.async { |task| yield task }
192
+ send_message message, validate: options[:validate]
193
+ { sent: message, collector: task.wait }
194
+ else
195
+ send_message message, validate: options[:validate]
196
+ return { sent: message }
197
+ end
201
198
  end
202
199
 
203
200
  def subscribe_to_status component_id, status_list, options={}
@@ -218,7 +215,7 @@ module RSMP
218
215
  "sS" => subscribe_list,
219
216
  'mId' => m_id
220
217
  })
221
- send_while_collecting message, options do |task|
218
+ send_and_collect_if_needed message, options do |task|
222
219
  collect_status_updates task, status_list, options[:collect].merge(m_id: m_id)
223
220
  end
224
221
  end
@@ -264,7 +261,7 @@ module RSMP
264
261
  "arg" => command_list,
265
262
  "mId" => m_id
266
263
  })
267
- send_while_collecting message, options do |task|
264
+ send_and_collect_if_needed message, options do |task|
268
265
  collect_command_responses task, command_list, options[:collect].merge(m_id: m_id)
269
266
  end
270
267
  end
@@ -352,19 +349,27 @@ module RSMP
352
349
  end
353
350
 
354
351
  def collect_status_updates task, status_list, options
355
- StatusUpdateMatcher.new(self, status_list, options).collect task
352
+ collector = StatusUpdateMatcher.new(self, status_list, options)
353
+ collector.collect task
354
+ collector
356
355
  end
357
356
 
358
357
  def collect_status_responses task, status_list, options
359
- StatusResponseMatcher.new(self, status_list, options).collect task
358
+ collector = StatusResponseMatcher.new(self, status_list, options)
359
+ collector.collect task
360
+ collector
360
361
  end
361
362
 
362
363
  def collect_command_responses task, command_list, options
363
- CommandResponseMatcher.new(self, command_list, options).collect task
364
+ collector = CommandResponseMatcher.new(self, command_list, options)
365
+ collector.collect task
366
+ collector
364
367
  end
365
368
 
366
369
  def collect_aggregated_status task, options
367
- AggregatedStatusMatcher.new(self, options).collect task
370
+ collector = AggregatedStatusMatcher.new(self, options)
371
+ collector.collect task
372
+ collector
368
373
  end
369
374
  end
370
375
  end
@@ -7,7 +7,6 @@ module RSMP
7
7
  attr_reader :rsmp_versions, :site_id, :supervisor_settings, :proxies, :logger
8
8
 
9
9
  def initialize options={}
10
-
11
10
  handle_supervisor_settings( options[:supervisor_settings] || {} )
12
11
  super options
13
12
  @proxies = []
@@ -161,7 +160,7 @@ module RSMP
161
160
  ip: info[:ip],
162
161
  port: info[:port],
163
162
  task: @task,
164
- settings: {'collect'=>@supervisor_settings['collect']},
163
+ collect: @collect,
165
164
  socket: socket,
166
165
  stream: stream,
167
166
  protocol: protocol,
@@ -244,10 +244,13 @@ module RSMP
244
244
  @status_subscriptions[component] ||= {}
245
245
  update_list[component] ||= {}
246
246
  now = Time.now # internal timestamp
247
+ subs = @status_subscriptions[component]
247
248
 
248
249
  message.attributes["sS"].each do |arg|
249
250
  sCI = arg["sCI"]
250
251
  subcription = {interval: arg["uRt"].to_i, last_sent_at: now}
252
+ subs[sCI] ||= {}
253
+ subs[sCI][arg["n"]] = subcription
251
254
  update_list[component][sCI] ||= []
252
255
  update_list[component][sCI] << arg["n"]
253
256
  end
@@ -279,11 +282,7 @@ module RSMP
279
282
  end
280
283
 
281
284
  def fetch_last_sent_status component, code, name
282
- if @last_status_sent && @last_status_sent[component] && @last_status_sent[component][code]
283
- @last_status_sent[component][code][name]
284
- else
285
- nil
286
- end
285
+ @last_status_sent.dig component, code, name
287
286
  end
288
287
 
289
288
  def store_last_sent_status message
@@ -307,10 +306,12 @@ module RSMP
307
306
  by_code.each_pair do |code,by_name|
308
307
  by_name.each_pair do |name,subscription|
309
308
  current = nil
309
+ should_send = false
310
310
  if subscription[:interval] == 0
311
311
  # send as soon as the data changes
312
312
  if component_object
313
313
  current, age = *(component_object.get_status code, name)
314
+ current = current.to_s
314
315
  end
315
316
  last_sent = fetch_last_sent_status component, code, name
316
317
  if current != last_sent
data/lib/rsmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = "0.7.2"
2
+ VERSION = "0.8.0"
3
3
  end
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.7.2
4
+ version: 0.8.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-11-18 00:00:00.000000000 Z
11
+ date: 2022-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -189,6 +189,7 @@ files:
189
189
  - ".gitignore"
190
190
  - ".gitmodules"
191
191
  - ".rspec"
192
+ - ".ruby-version"
192
193
  - CHANGELOG.md
193
194
  - Gemfile
194
195
  - Gemfile.lock
@@ -208,6 +209,7 @@ files:
208
209
  - lib/rsmp/collect/collector.rb
209
210
  - lib/rsmp/collect/listener.rb
210
211
  - lib/rsmp/collect/matcher.rb
212
+ - lib/rsmp/collect/message_collector.rb
211
213
  - lib/rsmp/collect/message_matchers.rb
212
214
  - lib/rsmp/collect/message_queries.rb
213
215
  - lib/rsmp/collect/notifier.rb
@@ -262,7 +264,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
262
264
  - !ruby/object:Gem::Version
263
265
  version: '0'
264
266
  requirements: []
265
- rubygems_version: 3.2.15
267
+ rubygems_version: 3.2.32
266
268
  signing_key:
267
269
  specification_version: 4
268
270
  summary: RoadSide Message Protocol (RSMP) library.