class MQTT::MQTT::MQTT::Packet

Class representing a MQTT Packet Performs binary encoding and decoding of headers

Constants

ATTR_DEFAULTS

Default attribute values

Attributes

body_length[R]

The length of the parsed packet body

flags[RW]

Array of 4 bits in the fixed header

id[RW]

Identifier to link related control packets together

version[RW]

The version number of the MQTT protocol to use (default 3.1.0)

Public Class Methods

create_from_header(byte) click to toggle source

Create a new packet object from the first byte of a MQTT packet

# File lib/mqttbridge/packet.rb, line 102
def self.create_from_header(byte)
  # Work out the class
  type_id = ((byte & 0xF0) >> 4)
  packet_class = MQTT::PACKET_TYPES[type_id]
  if packet_class.nil?
    raise ProtocolException.new("Invalid packet type identifier: #{type_id}")
  end

  # Convert the last 4 bits of byte into array of true/false
  flags = (0..3).map { |i| byte & (2 ** i) != 0 }

  # Create a new packet object
  packet_class.new(:flags => flags)
end
new(args={}) click to toggle source

Create a new empty packet

# File lib/mqttbridge/packet.rb, line 118
def initialize(args={})
  # We must set flags before the other values
  @flags = [false, false, false, false]
  update_attributes(ATTR_DEFAULTS.merge(args))
end
parse(buffer) click to toggle source

Parse buffer into new packet object

# File lib/mqttbridge/packet.rb, line 59
def self.parse(buffer)
  packet = parse_header(buffer)
  packet.parse_body(buffer)
  return packet
end
parse_header(buffer) click to toggle source

Parse the header and create a new packet object of the correct type The header is removed from the buffer passed into this function

# File lib/mqttbridge/packet.rb, line 67
def self.parse_header(buffer)
  # Check that the packet is a long as the minimum packet size
  if buffer.bytesize < 2
    raise ProtocolException.new("Invalid packet: less than 2 bytes long")
  end

  # Create a new packet object
  bytes = buffer.unpack("C5")
  packet = create_from_header(bytes.first)
  packet.validate_flags

  # Parse the packet length
  body_length = 0
  multiplier = 1
  pos = 1
  begin
    if buffer.bytesize <= pos
      raise ProtocolException.new("The packet length header is incomplete")
    end
    digit = bytes[pos]
    body_length += ((digit & 0x7F) * multiplier)
    multiplier *= 0x80
    pos += 1
  end while ((digit & 0x80) != 0x00) and pos <= 4

  # Store the expected body length in the packet
  packet.instance_variable_set('@body_length', body_length)

  # Delete the fixed header from the raw packet passed in
  buffer.slice!(0...pos)

  return packet
end
read(socket) click to toggle source

Read in a packet from a socket

# File lib/mqttbridge/packet.rb, line 28
    def self.read(socket)
      # Read in the packet header and create a new packet object
      packet = create_from_header(
        read_byte(socket)
      )
      
      
      packet.validate_flags
      # Read in the packet length
      multiplier = 1
      body_length = 0
      pos = 1
      begin
        digit = read_byte(socket)
        body_length += ((digit & 0x7F) * multiplier)
        multiplier *= 0x80
        pos += 1
      end while ((digit & 0x80) != 0x00) and pos <= 4

      # Store the expected body length in the packet
      packet.instance_variable_set('@body_length', body_length)

      # Read in the packet body
        $subackret=packet.parse_body(socket.read(body_length))
        #~ puts $subackret
#     puts @return_codes
      #~ puts $subackret
      return packet
    end

Public Instance Methods

body_length=(arg) click to toggle source

Set the length of the packet body

# File lib/mqttbridge/packet.rb, line 158
def body_length=(arg)
  @body_length = arg.to_i
end
encode_body() click to toggle source

Get serialisation of packet's body (variable header and payload)

# File lib/mqttbridge/packet.rb, line 172
def encode_body
  '' # No body by default
end
inspect() click to toggle source

Returns a human readable string

# File lib/mqttbridge/packet.rb, line 219
def inspect
  "\#<#{self.class}>"
end
message_id() click to toggle source

@deprecated Please use {#id} instead

# File lib/mqttbridge/packet.rb, line 1081
def message_id
  id
end
message_id=(args) click to toggle source

@deprecated Please use {#id=} instead

# File lib/mqttbridge/packet.rb, line 1086
def message_id=(args)
  self.id = args
end
parse_body(buffer) click to toggle source

Parse the body (variable header and payload) of a packet

# File lib/mqttbridge/packet.rb, line 163
def parse_body(buffer)
  if buffer.bytesize != body_length
    raise ProtocolException.new(
      "Failed to parse packet - input buffer (#{buffer.bytesize}) is not the same as the body length header (#{body_length})"
    )
  end
end
to_s() click to toggle source

Serialise the packet

# File lib/mqttbridge/packet.rb, line 178
def to_s
  # Encode the fixed header
  header = [
    ((type_id.to_i & 0x0F) << 4) |
    (flags[3] ? 0x8 : 0x0) |
    (flags[2] ? 0x4 : 0x0) |
    (flags[1] ? 0x2 : 0x0) |
    (flags[0] ? 0x1 : 0x0)
  ]

  # Get the packet's variable header and payload
  body = self.encode_body

  # Check that that packet isn't too big
  body_length = body.bytesize
  if body_length > 268435455
    raise "Error serialising packet: body is more than 256MB"
  end

  # Build up the body length field bytes
  begin
    digit = (body_length % 128)
    body_length = body_length.div(128)
    # if there are more digits to encode, set the top bit of this digit
    digit |= 0x80 if (body_length > 0)
    header.push(digit)
  end while (body_length > 0)

  # Convert header to binary and add on body
  header.pack('C*') + body
end
type_id() click to toggle source

Get the identifer for this packet type

# File lib/mqttbridge/packet.rb, line 136
def type_id
  index = MQTT::PACKET_TYPES.index(self.class)
  if index.nil?
    raise "Invalid packet type: #{self.class}"
  end
  return index
end
type_name() click to toggle source

Get the name of the packet type as a string in capitals (like the MQTT specification uses)

Example: CONNACK

# File lib/mqttbridge/packet.rb, line 148
def type_name
  self.class.name.split('::').last.upcase
end
update_attributes(attr={}) click to toggle source

Set packet attributes from a hash of attribute names and values

# File lib/mqttbridge/packet.rb, line 125
def update_attributes(attr={})
  attr.each_pair do |k,v|
    if v.is_a?(Array) or v.is_a?(Hash)
      send("#{k}=", v.dup)
    else
      send("#{k}=", v)
    end
  end
end
validate_flags() click to toggle source

Check that fixed header flags are valid for types that don't use the flags @private

# File lib/mqttbridge/packet.rb, line 212
def validate_flags
  if flags != [false, false, false, false]
    raise ProtocolException.new("Invalid flags in #{type_name} packet header")
  end
end
version=(arg) click to toggle source

Set the protocol version number

# File lib/mqttbridge/packet.rb, line 153
def version=(arg)
  @version = arg.to_s
end

Protected Instance Methods

encode_bits(bits) click to toggle source

Encode an array of bits and return them

# File lib/mqttbridge/packet.rb, line 231
def encode_bits(bits)
  [bits.map{|b| b ? '1' : '0'}.join].pack('b*')
end
encode_bytes(*bytes) click to toggle source

Encode an array of bytes and return them

# File lib/mqttbridge/packet.rb, line 226
def encode_bytes(*bytes)
  bytes.pack('C*')
end
encode_short(val) click to toggle source

Encode a 16-bit unsigned integer and return it

# File lib/mqttbridge/packet.rb, line 236
def encode_short(val)
  [val.to_i].pack('n')
end
encode_string(str) click to toggle source

Encode a UTF-8 string and return it (preceded by the length of the string)

# File lib/mqttbridge/packet.rb, line 242
def encode_string(str)
  str = str.to_s.encode('UTF-8')

  # Force to binary, when assembling the packet
  str.force_encoding('ASCII-8BIT')
  encode_short(str.bytesize) + str
end
shift_bits(buffer) click to toggle source

Remove 8 bits from the front of buffer

# File lib/mqttbridge/packet.rb, line 262
def shift_bits(buffer)
  buffer.slice!(0...1).unpack('b8').first.split('').map {|b| b == '1'}
end
shift_byte(buffer) click to toggle source

Remove one byte from the front of the string

# File lib/mqttbridge/packet.rb, line 257
def shift_byte(buffer)
  buffer.slice!(0...1).unpack('C').first
end
shift_data(buffer,bytes) click to toggle source

Remove n bytes from the front of buffer

# File lib/mqttbridge/packet.rb, line 267
def shift_data(buffer,bytes)
  buffer.slice!(0...bytes)
end
shift_short(buffer) click to toggle source

Remove a 16-bit unsigned integer from the front of buffer

# File lib/mqttbridge/packet.rb, line 251
def shift_short(buffer)
  bytes = buffer.slice!(0..1)
  bytes.unpack('n').first
end
shift_string(buffer) click to toggle source

Remove string from the front of buffer

# File lib/mqttbridge/packet.rb, line 272
def shift_string(buffer)
  len = shift_short(buffer)
  str = shift_data(buffer,len)
  # Strings in MQTT v3.1 are all UTF-8
  str.force_encoding('UTF-8')
end