Class: RSMP::StateCollector

Inherits:
Collector show all
Defined in:
lib/rsmp/collect/state_collector.rb

Overview

Base class for waiting for specific status or command responses, specified by a list of matchers. Matchers are defined as an array of hashes, e.g [

 {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"securityCode", "v"=>"1111"},
 {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"year", "v"=>"2020"},
 {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/}
]

Note that matchers can contain regex patterns for values, like /d+/ in the example above.

When an input messages is received it typically contains several items, eg: [

{"cCI"=>"M0104", "n"=>"month", "v"=>"9", "age"=>"recent"},
{"cCI"=>"M0104", "n"=>"day", "v"=>"29", "age"=>"recent"},
{"cCI"=>"M0104", "n"=>"hour", "v"=>"17", "age"=>"recent"}

]

Each input item is matched against each of the matchers. If a match is found, it’s stored in the @results hash, with the matcher as the key, and a mesage and status as the key. In the example above, this matcher:

“cO”=>“setDate”, “n”=>“month”, “v”=>/d+/

matches this input:

“n”=>“month”, “v”=>“9”, “age”=>“recent”

And the result is stored as:

{"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/ =>
  { <StatusResponse message>, "cO"=>"setDate", "n"=>"month", "v"=>"9" }

}

Direct Known Subclasses

CommandResponseCollector, StatusCollector

Instance Attribute Summary collapse

Attributes inherited from Collector

#condition, #error, #m_id, #status, #task

Attributes included from Logging

#archive, #logger

Instance Method Summary collapse

Methods inherited from Collector

#acceptable?, #cancel, #collect, #collect!, #complete, #do_stop, #incomplete, #inspect, #make_title, #ok!, #receive, #receive_disconnect, #receive_error, #receive_schema_error, #reject_not_ack, #reset, #start, #use_task, #wait, #wait!

Methods included from Logging

#author, #initialize_logging, #log

Methods included from Collector::Reporting

#describe_num_and_type, #describe_types, #identifier

Methods included from Collector::Status

#cancelled?, #collecting?, #ingoing?, #ok?, #outgoing?, #ready?, #timeout?

Methods included from Receiver

#accept_message?, #handle_message, #initialize_receiver, #receive, #receive_error, #reject_message?, #start_receiving, #stop_receiving

Methods included from Inspect

#inspect, #inspector

Constructor Details

#initialize(proxy, want, options = {}) ⇒ StateCollector

Initialize with a list of wanted statuses

Raises:

  • (ArgumentError)


38
39
40
41
42
43
# File 'lib/rsmp/collect/state_collector.rb', line 38

def initialize(proxy, want, options = {})
  raise ArgumentError, 'num option cannot be used' if options[:num]

  super(proxy, options)
  @matchers = want.map { |item| build_matcher item }
end

Instance Attribute Details

#matchersObject (readonly)

Returns the value of attribute matchers.



35
36
37
# File 'lib/rsmp/collect/state_collector.rb', line 35

def matchers
  @matchers
end

Instance Method Details

#add_command_result(hash, want, got) ⇒ Object



215
216
217
218
219
220
221
222
223
# File 'lib/rsmp/collect/state_collector.rb', line 215

def add_command_result(hash, want, got)
  cci = want['cCI']
  hash[cci] ||= {}
  co = want['cO']
  hash[cci][co] ||= {}
  n = want['n']
  v = got ? got['v'] : nil
  hash[cci][co][n] = v
end

#add_command_want_to_hash(hash, item) ⇒ Object



197
198
199
200
201
202
203
204
205
# File 'lib/rsmp/collect/state_collector.rb', line 197

def add_command_want_to_hash(hash, item)
  cci = item['cCI']
  hash[cci] ||= {}
  co = item['cO']
  hash[cci][co] ||= {}
  n = item['n']
  v = item['v']
  hash[cci][co][n] = v || :any
end

#add_status_result(hash, want, got) ⇒ Object



225
226
227
228
229
230
231
# File 'lib/rsmp/collect/state_collector.rb', line 225

def add_status_result(hash, want, got)
  sci = want['sCI']
  hash[sci] ||= {}
  n = want['n']
  s = got ? got['s'] : nil
  hash[sci][n] = s
end

#add_status_want_to_hash(hash, item) ⇒ Object



207
208
209
210
211
212
213
# File 'lib/rsmp/collect/state_collector.rb', line 207

def add_status_want_to_hash(hash, item)
  sci = item['sCI']
  hash[sci] ||= {}
  n = item['n']
  s = item['s']
  hash[sci][n] = s || :any
end

#build_matcher(want) ⇒ Object

Build a matcher object. Sub-classes should override to use their own matcher classes.



47
48
49
# File 'lib/rsmp/collect/state_collector.rb', line 47

def build_matcher(want)
  Matcher.new want
end

#describeObject



128
129
130
# File 'lib/rsmp/collect/state_collector.rb', line 128

def describe
  @matchers.map { |q| q.want.to_s }
end

#describe_matcherObject

return a string that describes the attributes that we’re looking for



133
134
135
# File 'lib/rsmp/collect/state_collector.rb', line 133

def describe_matcher
  "#{super} matching #{matcher_want_hash}"
end

#describe_progressObject

return a string that describe how many many messages have been collected



178
179
180
181
182
# File 'lib/rsmp/collect/state_collector.rb', line 178

def describe_progress
  num_matchers = @matchers.size
  num_matched =  @matchers.count(&:done?)
  ".. Matched #{num_matched}/#{num_matchers} with #{progress_hash}"
end

#done?Boolean

Are there matchers left to type_match?

Returns:

  • (Boolean)


77
78
79
# File 'lib/rsmp/collect/state_collector.rb', line 77

def done?
  @matchers.all?(&:done?)
end

#handle_match_result(matched, matcher, message, item) ⇒ Object



98
99
100
101
102
103
104
105
106
107
# File 'lib/rsmp/collect/state_collector.rb', line 98

def handle_match_result(matched, matcher, message, item)
  return if matched.nil?

  log_match_result(message, matched, matcher, item)
  if matched == true
    matcher.keep message, item
  elsif matched == false
    matcher.forget
  end
end

#keep(message) ⇒ Object

don’t collect anything. Matcher will collect them instead



126
# File 'lib/rsmp/collect/state_collector.rb', line 126

def keep(message); end

#log_completeObject

log when we end collecting



248
249
250
# File 'lib/rsmp/collect/state_collector.rb', line 248

def log_complete
  @distributor.log "#{identifier}: Completed with #{matcher_got_hash}", level: :collect
end

#log_match_result(message, matched, matcher, item) ⇒ Object



92
93
94
95
96
# File 'lib/rsmp/collect/state_collector.rb', line 92

def log_match_result(message, matched, matcher, item)
  type = { true => 'match', false => 'mismatch' }[matched]
  @distributor.log "#{@title.capitalize} #{message.m_id_short} collect #{type} #{matcher.want}, item #{item}",
                   level: :debug
end

#matcher_got_hashObject



233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/rsmp/collect/state_collector.rb', line 233

def matcher_got_hash
  h = {}
  @matchers.each do |matcher|
    want = matcher.want
    got = matcher.got
    if want['cCI']
      add_command_result(h, want, got)
    elsif want['sCI']
      add_status_result(h, want, got)
    end
  end
  h
end

#matcher_result(want) ⇒ Object

Get a results



52
53
54
55
56
57
# File 'lib/rsmp/collect/state_collector.rb', line 52

def matcher_result(want)
  matcher = @matchers.find { |q| q.want == want }
  raise unless matcher

  matcher.got
end

#matcher_statusObject

Get a simplified hash of matchers, with values set to either true or false, indicating which matchers have been matched.



83
84
85
# File 'lib/rsmp/collect/state_collector.rb', line 83

def matcher_status
  @matchers.to_h { |matcher| [matcher.want, matcher.done?] }
end

#matcher_want_hashObject



184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/rsmp/collect/state_collector.rb', line 184

def matcher_want_hash
  h = {}
  @matchers.each do |matcher|
    item = matcher.want
    if item['cCI']
      add_command_want_to_hash(h, item)
    elsif item['sCI']
      add_status_want_to_hash(h, item)
    end
  end
  h
end

#messagesObject

Get messages from results



65
66
67
# File 'lib/rsmp/collect/state_collector.rb', line 65

def messages
  @matchers.map(&:message).uniq.compact
end

#perform_match(message) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/rsmp/collect/state_collector.rb', line 109

def perform_match(message)
  return false if super == false
  return unless collecting?

  @matchers.each do |matcher|
    break unless collecting?

    get_items(message).each do |item|
      matched = matcher.perform_match(item, message, @block)
      break unless collecting?

      handle_match_result(matched, matcher, message, item)
    end
  end
end

#progressObject

Return progress as completes matchers vs. total number of matchers



70
71
72
73
74
# File 'lib/rsmp/collect/state_collector.rb', line 70

def progress
  need = @matchers.size
  reached = @matchers.count(&:done?)
  { need: need, reached: reached }
end

#progress_hashObject

return a hash that describe the status of all matchers



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/rsmp/collect/state_collector.rb', line 138

def progress_hash
  h = {}
  @matchers.each do |matcher|
    want = matcher.want
    if want['cCI']
      process_command_matcher(h, matcher, want)
    elsif want['sCI']
      process_status_matcher(h, matcher, want)
    end
  end
  h
end

#reachedObject

Get an array of the last item received for each matcher



60
61
62
# File 'lib/rsmp/collect/state_collector.rb', line 60

def reached
  @matchers.map(&:got).compact
end

#summaryObject

Get a simply array of bools, showing which matchers have been matched.



88
89
90
# File 'lib/rsmp/collect/state_collector.rb', line 88

def summary
  @matchers.map(&:done?)
end