class Vissen::Input::Message::Base
This is the base message implementaion. This class should never be used directly, but rather be subclassed to create the various messages that the system understands.
The Base
class keeps track of subclasses and can produce a message factory for all the implementations that it knows about (see `.factory`).
Constants
Public Class Methods
Alias to `#matcher` that swaps named arguments for positional ones.
@see matcher
@param (see matcher) @return (see matcher)
# File lib/vissen/input/message/base.rb, line 98 def [](channel, number = nil) matcher channel: channel, number: number end
Build a new instance of `Message::Base`, or a subclass, using more intuitive arguments. Subclasses of Base
can utilize the same functionality by simply redefining `DATA_LENGTH` to correspond to their message length.
Note
that status and channel are masked using the default masks, and not the constants that may have been defined by a subclass.
@param bytes [Array<Integer>] the message data byte values.
Unspecified values default to 0
@param status [Integer] the status to use for the new message. @param channel [Integer] the channel to use for the new message. @param timestamp [Float] the timestamp to use for the new message. @return [Base] a new instance of this class.
# File lib/vissen/input/message/base.rb, line 116 def create(*bytes, status: self::STATUS, channel: 0, timestamp: Time.now.to_f) raise ArgumentError if bytes.length >= self::DATA_LENGTH validate_status status validate_channel channel data = Array.new self::DATA_LENGTH, 0 # Note: this line line must reference # STATUS_MASK and not self::STATUS_MASK data[0] = (status & STATUS_MASK) + (channel & CHANNEL_MASK) # Copy the bytes bytes.each_with_index { |value, index| data[index + 1] = value } new data, timestamp end
Creates a new factory with all the subclasses of base added to it as matchers.
@return [MessageFactory] a factory configured to build all
subclasses of Base.
# File lib/vissen/input/message/base.rb, line 87 def factory raise RuntimeError unless defined? @subclasses MessageFactory.new @subclasses.map(&:matcher) end
Returns a new instance of a Matcher
, configured to match this particular Message
class. Subclasses of Base
can utilize the same functionality by simply redefining STATUS
and, if necessary, STATUS_MASK.
By supplying the optional named arguments channel and number
Raises a RangeError if the channel is given and is outside its valid range of (0..15). Raises a RangeError if number is given and is outside its valid range of (0..127).
@param channel [nil, Integer] the channel to match, or nil to match
all channels.
@param number [nil, Integer] the second byte value to match, or nil
to match all values.
@return [Matcher] the matcher that fulfills the requirements.
# File lib/vissen/input/message/base.rb, line 50 def matcher(channel: nil, number: nil) return klass_matcher unless channel || number val, mask = status_value_and_mask channel if number raise RangeError unless (0...128).cover? number Matcher.new(self) { |d| (d[0] & mask) == val && d[1] == number } else Matcher.new(self) { |d| (d[0] & mask) == val } end end
Protected Class Methods
Called automatically by inheriting classes.
@param subclass [Base] the inheriting class. @return [nil]
# File lib/vissen/input/message/base.rb, line 142 def inherited(subclass) (@subclasses ||= []) << subclass nil end
The klass matcher is the most generic `Matcher` for the message class and is cached to avoid duplication. By default the default `Matcher` uses the value and mask returned by `#status_value_and_mask` to match messages. Subclasses that need different behaviour can pass a block to be forwarded directly to the matcher (see Matcher.new
).
@param block [Proc] the block that should be passed to
`Matcher.new` when first creating the matcher.
@return [Matcher] a matcher that matches all messages of this type.
# File lib/vissen/input/message/base.rb, line 204 def klass_matcher(&block) return @klass_matcher if defined?(@klass_matcher) unless block_given? val, mask = status_value_and_mask block = proc { |d| (d[0] & mask) == val } end @klass_matcher = Matcher.new(self, &block) end
Creates a value and mask that can be used to match the first byte of a message.
Usage¶ ↑
The following example illustrates how the value and mask are intended to be used.
value, mask = status_value_and_mask 0x42 & mask == value # => true if STATUS == 0x40 value, mask = status_value_and_mask 3 0x42 & mask == value # => false since channel is 2
@param channel [Integer] the channel to match. @return [Array<Integer>] a value and a mask.
# File lib/vissen/input/message/base.rb, line 184 def status_value_and_mask(channel = nil) if channel validate_channel channel [(self::STATUS & self::STATUS_MASK) + (channel & CHANNEL_MASK), self::STATUS_MASK + CHANNEL_MASK] else [self::STATUS, self::STATUS_MASK] end end
Helper method to validate a channel value.
@raise [RangeError] if the channel is outside its allowable range.
@param channel [Integer] the channel to validate. @return [true] when the channel is valid.
# File lib/vissen/input/message/base.rb, line 164 def validate_channel(channel) raise RangeError unless (channel & ~CHANNEL_MASK).zero? true end
Helper method to validate a status value.
@raise [RangeError] if the status is outside its allowable range.
@param status [Integer] the status to validate. @return [true] when the status is valid.
# File lib/vissen/input/message/base.rb, line 153 def validate_status(status) raise RangeError unless (status & ~STATUS_MASK).zero? true end
Public Instance Methods
Checks message data consistency with the class default matcher.
@return [true, false] true if the message data matches the class
matcher.
# File lib/vissen/input/message/base.rb, line 25 def valid? self.class.matcher.match? data end