class SDN::Message

Attributes

ack_requested[RW]
dest[RW]
node_type[RW]
src[RW]

Public Class Methods

expected_response?(message) click to toggle source
# File lib/sdn/message.rb, line 14
def expected_response?(message)
  if name =~ /::Get([A-Za-z]+)/
    message.class.name == name.sub("::Get", "::Post")
  else
    message.is_a?(Ack) || message.is_a?(Nack)
  end
end
inherited(klass) click to toggle source
# File lib/sdn/message.rb, line 8
def inherited(klass)
  return Message.inherited(klass) unless self == Message
  @message_map = nil
  (@subclasses ||= []) << klass
end
new(node_type: nil, ack_requested: false, src: nil, dest: nil) click to toggle source
# File lib/sdn/message.rb, line 83
def initialize(node_type: nil, ack_requested: false, src: nil, dest: nil)
  @node_type = node_type || 0
  @ack_requested = ack_requested
  if src.nil? && !dest.nil? && is_group_address?(dest)
    src = dest
    dest = nil
  end
  @src = src || [0, 0, 1]
  @dest = dest || [0, 0, 0]
end
parse(data) click to toggle source
# File lib/sdn/message.rb, line 22
def parse(data)
  offset = -1
  msg = length = ack_requested = message_class = nil
  # we loop here scanning for a valid message
  loop do
    offset += 1
    # give these weird messages a chance
    result = ILT2::MasterControl.parse(data)
    return result if result

    return [nil, 0] if data.length - offset < 11
    msg = to_number(data[offset])
    length = to_number(data[offset + 1])
    ack_requested = length & 0x80 == 0x80
    length &= 0x7f
    # impossible message
    next if length < 11 || length > 43
    # don't have enough data for what this message wants;
    # it could be garbage on the line so keep scanning
    next if length > data.length - offset

    message_class = message_map[msg] || UnknownMessage

    calculated_sum = checksum(data.slice(offset, length - 2))
    read_sum = data.slice(offset + length - 2, 2)
    next unless read_sum == calculated_sum

    break
  end

  node_type = node_type_from_number(to_number(data[offset + 2]))
  src = transform_param(data.slice(offset + 3, 3))
  dest = transform_param(data.slice(offset + 6, 3))
  begin
    result = message_class.new(node_type: node_type, ack_requested: ack_requested, src: src, dest: dest)
    result.parse(data.slice(offset + 9, length - 11))
    result.msg = msg if message_class == UnknownMessage
  rescue ArgumentError => e
    SDN.logger.warn "discarding illegal message of type #{message_class.name}: #{e}"
    result = nil
  end
  [result, offset + length]
end

Private Class Methods

message_map() click to toggle source
# File lib/sdn/message.rb, line 68
def message_map
  @message_map ||=
    @subclasses.inject({}) do |memo, klass|
      next memo unless klass.constants(false).include?(:MSG)
      memo[klass.const_get(:MSG, false)] = klass
      memo
    end
end

Public Instance Methods

==(other) click to toggle source
# File lib/sdn/message.rb, line 107
def ==(other)
  self.serialize == other.serialize
end
class_inspect() click to toggle source
# File lib/sdn/message.rb, line 115
def class_inspect
  ivars = instance_variables - [:@node_type, :@ack_requested, :@src, :@dest, :@params]
  return if ivars.empty?
  ivars.map { |iv| ", #{iv}=#{instance_variable_get(iv).inspect}" }.join
end
inspect() click to toggle source
# File lib/sdn/message.rb, line 111
def inspect
  "#<%s @node_type=%s, @ack_requested=%s, @src=%s, @dest=%s%s>" % [self.class.name, node_type_to_string(node_type), ack_requested, print_address(src), print_address(dest), class_inspect]
end
parse(params) click to toggle source
# File lib/sdn/message.rb, line 94
def parse(params)
  raise MalformedMessage, "unrecognized params for #{self.class.name}: #{params.map { |b| '%02x' % b }}" if self.class.const_defined?(:PARAMS_LENGTH) && params.length != self.class.const_get(:PARAMS_LENGTH)
end
serialize() click to toggle source
# File lib/sdn/message.rb, line 98
def serialize
  result = transform_param(node_type_to_number(node_type)) + transform_param(src) + transform_param(dest) + params
  length = result.length + 4
  length |= 0x80 if ack_requested
  result = transform_param(self.class.const_get(:MSG)) + transform_param(length) + result
  result.concat(checksum(result))
  result.pack("C*")
end

Protected Instance Methods

params() click to toggle source
# File lib/sdn/message.rb, line 123
def params; []; end