text: '''
  Encapsulated Interface (MEI) Transport Messages
  -----------------------------------------------
  
  '''
  import struct
  from pymodbus.constants import DeviceInformation, MoreData
  from pymodbus.pdu import ModbusRequest
  from pymodbus.pdu import ModbusResponse
  from pymodbus.device import ModbusControlBlock
  from pymodbus.device import DeviceInformationFactory
  from pymodbus.pdu import ModbusExceptions as merror
  
  _MCB = ModbusControlBlock()
  
  
  #---------------------------------------------------------------------------#
  # Read Device Information
  #---------------------------------------------------------------------------#
  class ReadDeviceInformationRequest(ModbusRequest):
      '''
      This function code allows reading the identification and additional
      information relative to the physical and functional description of a
      remote device, only.
  
      The Read Device Identification interface is modeled as an address space
      composed of a set of addressable data elements. The data elements are
      called objects and an object Id identifies them.
      '''
      function_code = 0x2b
      sub_function_code = 0x0e
      _rtu_frame_size = 7
  
      def __init__(self, read_code=None, object_id=0x00, **kwargs):
          ''' Initializes a new instance
  
          :param read_code: The device information read code
          :param object_id: The object to read from
          '''
          ModbusRequest.__init__(self, **kwargs)
          self.read_code = read_code or DeviceInformation.Basic
          self.object_id = object_id
  
      def encode(self):
          ''' Encodes the request packet
  
          :returns: The byte encoded packet
          '''
          packet = struct.pack('>BBB', self.sub_function_code,
              self.read_code, self.object_id)
          return packet
  
      def decode(self, data):
          ''' Decodes data part of the message.
  
          :param data: The incoming data
          '''
          params = struct.unpack('>BBB', data)
          self.sub_function_code, self.read_code, self.object_id = params
  
      def execute(self, context):
          ''' Run a read exeception status request against the store
  
          :param context: The datastore to request from
          :returns: The populated response
          '''
          if not (0x00 <= self.object_id <= 0xff):
              return self.doException(merror.IllegalValue)
          if not (0x00 <= self.read_code <= 0x04):
              return self.doException(merror.IllegalValue)
  
          information = DeviceInformationFactory.get(_MCB,
              self.read_code, self.object_id)
          return ReadDeviceInformationResponse(self.read_code, information)
  
      def __str__(self):
          ''' Builds a representation of the request
  
          :returns: The string representation of the request
          '''
          params = (self.read_code, self.object_id)
          return "ReadDeviceInformationRequest(%d,%d)" % params
  
  
  class ReadDeviceInformationResponse(ModbusResponse):
      '''
      '''
      function_code = 0x2b
      sub_function_code = 0x0e
  
      @classmethod
      def calculateRtuFrameSize(cls, buffer):
          ''' Calculates the size of the message
  
          :param buffer: A buffer containing the data that have been received.
          :returns: The number of bytes in the response.
          '''
          size  = 8 # skip the header information
          count = struct.unpack('>B', buffer[7])[0]
  
          while count > 0:
              _, object_length = struct.unpack('>BB', buffer[size:size+2])
              size += object_length + 2
              count -= 1
          return size + 2
  
      def __init__(self, read_code=None, information=None, **kwargs):
          ''' Initializes a new instance
  
          :param read_code: The device information read code
          :param information: The requested information request
          '''
          ModbusResponse.__init__(self, **kwargs)
          self.read_code = read_code or DeviceInformation.Basic
          self.information = information or {}
          self.number_of_objects = len(self.information)
          self.conformity = 0x83 # I support everything right now
  
          # TODO calculate
          self.next_object_id = 0x00 # self.information[-1](0)
          self.more_follows = MoreData.Nothing
  
      def encode(self):
          ''' Encodes the response
  
          :returns: The byte encoded message
          '''
          packet = struct.pack('>BBBBBB', self.sub_function_code,
              self.read_code, self.conformity, self.more_follows,
              self.next_object_id, self.number_of_objects)
  
          for (object_id, data) in self.information.iteritems():
              packet += struct.pack('>BB', object_id, len(data))
              packet += data
  
          return packet
  
      def decode(self, data):
          ''' Decodes a the response
  
          :param data: The packet data to decode
          '''
          params = struct.unpack('>BBBBBB', data[0:6])
          self.sub_function_code, self.read_code = params[0:2]
          self.conformity, self.more_follows = params[2:4]
          self.next_object_id, self.number_of_objects = params[4:6]
          self.information, count = {}, 6 # skip the header information
  
          while count < len(data):
              object_id, object_length = struct.unpack('>BB', data[count:count+2])
              count += object_length + 2
              self.information[object_id] = data[count-object_length:count]
  
      def __str__(self):
          ''' Builds a representation of the response
  
          :returns: The string representation of the response
          '''
          return "ReadDeviceInformationResponse(%d)" % self.read_code
  
  #---------------------------------------------------------------------------#
  # Exported symbols
  #---------------------------------------------------------------------------#
  __all__ = [
      "ReadDeviceInformationRequest", "ReadDeviceInformationResponse",
  ]
  