Пример #1
0
    def _write_multiple_coils(self, request_pdu):
        """execute modbus function 15"""
        call_hooks("modbus.Slave.handle_write_multiple_coils_request", (self, request_pdu))
        # get the starting address and the number of items from the request pdu
        (starting_address, quantity_of_x, byte_count) = struct.unpack(">HHB", request_pdu[1:6])

        expected_byte_count = quantity_of_x // 8
        if (quantity_of_x % 8) > 0:
            expected_byte_count += 1

        if (quantity_of_x <= 0) or (quantity_of_x > 1968) or (byte_count != expected_byte_count):
            # maximum allowed size is 1968 coils
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.COILS, starting_address, quantity_of_x)

        count = 0
        for i in range(byte_count):
            if count >= quantity_of_x:
                break
            fmt = "B" if self.unsigned else "b"
            (byte_value, ) = struct.unpack(">"+fmt, request_pdu[6+i])
            for j in range(8):
                if count >= quantity_of_x:
                    break

                if byte_value & (1 << j):
                    block[offset+i*8+j] = 1
                else:
                    block[offset+i*8+j] = 0

                count += 1
        return struct.pack(">HH", starting_address, count)
Пример #2
0
    def _write_multiple_registers(self, request_pdu):
        """execute modbus function 16"""
        call_hooks("modbus.Slave.handle_write_multiple_registers_request",
                   (self, request_pdu))
        # get the starting address and the number of items from the request pdu
        (starting_address, quantity_of_x,
         byte_count) = struct.unpack(">HHB", request_pdu[1:6])

        if (quantity_of_x <= 0) or (quantity_of_x >
                                    123) or (byte_count !=
                                             (quantity_of_x * 2)):
            # maximum allowed size is 123 registers in one reading
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS,
                                                   starting_address,
                                                   quantity_of_x)

        count = 0
        for i in range(quantity_of_x):
            count += 1
            fmt = "H" if self.unsigned else "h"
            block[offset + i] = struct.unpack(
                ">" + fmt, request_pdu[6 + 2 * i:8 + 2 * i])[0]

        return struct.pack(">HH", starting_address, count)
Пример #3
0
    def _read_registers(self, block_type, request_pdu):
        """read the value of holding and input registers"""
        (starting_address,
         quantity_of_x) = struct.unpack(">HH", request_pdu[1:5])

        if (quantity_of_x <= 0) or (quantity_of_x > 125):
            # maximum allowed size is 125 registers in one reading
            LOGGER.debug("quantity_of_x is %d", quantity_of_x)
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(block_type,
                                                   starting_address,
                                                   quantity_of_x)

        # get the values
        values = block[offset:offset + quantity_of_x]

        # write the response header
        response = struct.pack(">B", 2 * quantity_of_x)
        # add the values of every register on 2 bytes
        for reg in values:
            fmt = "H" if self.unsigned else "h"
            response += struct.pack(">" + fmt, reg)
        return response
Пример #4
0
 def _get_block_and_offset(self, block_type, address, length):
     """returns the block and offset corresponding to the given address"""
     for block in self._memory[block_type]:
         if address >= block.starting_address:
             offset = address - block.starting_address
             if block.size >= offset + length:
                 return block, offset
     raise ModbusError(defines.ILLEGAL_DATA_ADDRESS)
Пример #5
0
    def _read_write_multiple_registers(self, request_pdu):
        """execute modbus function 23"""
        call_hooks("modbus.Slave.handle_read_write_multiple_registers_request", (self, request_pdu))
        # get the starting address and the number of items from the request pdu
        (starting_read_address, quantity_of_x_to_read, starting_write_address, quantity_of_x_to_write, byte_count_to_write) = struct.unpack(">HHHHB", request_pdu[1:10])

        # read part
        if (quantity_of_x_to_read <= 0) or (quantity_of_x_to_read > 125):
            # maximum allowed size is 125 registers in one reading
            LOGGER.debug("quantity_of_x_to_read is %d", quantity_of_x_to_read)
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_read_address, quantity_of_x_to_read)

        # get the values
        values = block[offset:offset+quantity_of_x_to_read]
        # write the response header
        response = struct.pack(">B", 2 * quantity_of_x_to_read)
        # add the values of every register on 2 bytes
        for reg in values:
            fmt = "H" if self.unsigned else "h"
            response += struct.pack(">"+fmt, reg)

        # write part
        if (quantity_of_x_to_write <= 0) or (quantity_of_x_to_write > 123) or (byte_count_to_write != (quantity_of_x_to_write * 2)):
            # maximum allowed size is 123 registers in one reading
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        # look for the block corresponding to the request
        block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_write_address, quantity_of_x_to_write)

        count = 0
        for i in range(quantity_of_x_to_write):
            count += 1
            fmt = "H" if self.unsigned else "h"
            block[offset+i] = struct.unpack(">"+fmt, request_pdu[10+2*i:12+2*i])[0]       
        
        return response
Пример #6
0
    def _write_single_coil(self, request_pdu):
        """execute modbus function 5"""

        call_hooks("modbus.Slave.handle_write_single_coil_request", (self, request_pdu))
        (data_address, value) = struct.unpack(">HH", request_pdu[1:5])
        block, offset = self._get_block_and_offset(defines.COILS, data_address, 1)
        if value == 0:
            block[offset] = 0
        elif value == 0xff00:
            block[offset] = 1
        else:
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)
        # returns echo of the command
        return request_pdu[1:]
Пример #7
0
    def handle_request(self, request_pdu, broadcast=False):
        """
        parse the request pdu, makes the corresponding action
        and returns the response pdu
        """
        # thread-safe
        with self._data_lock:
            try:
                retval = call_hooks("modbus.Slave.handle_request",
                                    (self, request_pdu))
                if retval is not None:
                    return retval

                # get the function code
                (function_code, ) = struct.unpack(">B", request_pdu[0:1])

                # check if the function code is valid. If not returns error response
                if function_code not in self._fn_code_map:
                    raise ModbusError(defines.ILLEGAL_FUNCTION)

                # if read query is broadcasted raises an error
                cant_be_broadcasted = (defines.READ_COILS,
                                       defines.READ_DISCRETE_INPUTS,
                                       defines.READ_INPUT_REGISTERS,
                                       defines.READ_HOLDING_REGISTERS)
                if broadcast and (function_code in cant_be_broadcasted):
                    raise ModbusInvalidRequestError(
                        "Function %d can not be broadcasted" % function_code)

                # execute the corresponding function
                response_pdu = self._fn_code_map[function_code](request_pdu)
                if response_pdu:
                    if broadcast:
                        call_hooks("modbus.Slave.on_handle_broadcast",
                                   (self, response_pdu))
                        LOGGER.debug("broadcast: %s",
                                     get_log_buffer("!!", response_pdu))
                        return ""
                    else:
                        return struct.pack(">B", function_code) + response_pdu
                raise Exception("No response for function %d" % function_code)

            except ModbusError as excpt:
                LOGGER.debug(str(excpt))
                call_hooks("modbus.Slave.on_exception",
                           (self, function_code, excpt))
                return struct.pack(">BB", function_code + 128,
                                   excpt.get_exception_code())
Пример #8
0
    def _read_digital(self, block_type, request_pdu):
        """read the value of coils and discrete inputs"""
        (starting_address,
         quantity_of_x) = struct.unpack(">HH", request_pdu[1:5])

        if (quantity_of_x <= 0) or (quantity_of_x > 2000):
            # maximum allowed size is 2000 bits in one reading
            raise ModbusError(defines.ILLEGAL_DATA_VALUE)

        block, offset = self._get_block_and_offset(block_type,
                                                   starting_address,
                                                   quantity_of_x)

        values = block[offset:offset + quantity_of_x]

        # pack bits in bytes
        byte_count = quantity_of_x // 8
        if (quantity_of_x % 8) > 0:
            byte_count += 1

        # write the response header
        response = struct.pack(">B", byte_count)

        i, byte_value = 0, 0
        for coil in values:
            if coil:
                byte_value += (1 << i)
            if i >= 7:
                # write the values of 8 bits in a byte
                response += struct.pack(">B", byte_value)
                # reset the counters
                i, byte_value = 0, 0
            else:
                i += 1

        # if there is remaining bits: add one more byte with their values
        if i > 0:
            fmt = "B" if self.unsigned else "b"
            response += struct.pack(">" + fmt, byte_value)
        return response
Пример #9
0
    def execute(
        self, slave, function_code, starting_address, quantity_of_x=0, output_value=0, data_format="", expected_length=-1):
        """
        Execute a modbus query and returns the data part of the answer as a tuple
        The returned tuple depends on the query function code. see modbus protocol
        specification for details
        data_format makes possible to extract the data like defined in the
        struct python module documentation
        """

        pdu = ""
        is_read_function = False
        nb_of_digits = 0

        # open the connection if it is not already done
        self.open()

        # Build the modbus pdu and the format of the expected data.
        # It depends of function code. see modbus specifications for details.
        if function_code == defines.READ_COILS or function_code == defines.READ_DISCRETE_INPUTS:
            is_read_function = True
            pdu = struct.pack(">BHH", function_code, starting_address, quantity_of_x)
            byte_count = quantity_of_x // 8
            if (quantity_of_x % 8) > 0:
                byte_count += 1
            nb_of_digits = quantity_of_x
            if not data_format:
                data_format = ">" + (byte_count * "B")
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + bytcodeLen + bytecode + crc1 + crc2
                expected_length = byte_count + 5

        elif function_code == defines.READ_INPUT_REGISTERS or function_code == defines.READ_HOLDING_REGISTERS:
            is_read_function = True
            pdu = struct.pack(">BHH", function_code, starting_address, quantity_of_x)
            if not data_format:
                data_format = ">" + (quantity_of_x * "H")
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2
                expected_length = 2 * quantity_of_x + 5

        elif (function_code == defines.WRITE_SINGLE_COIL) or (function_code == defines.WRITE_SINGLE_REGISTER):
            if function_code == defines.WRITE_SINGLE_COIL:
                if output_value != 0:
                    output_value = 0xff00
            fmt = ">BH"+("H" if output_value >= 0 else "h")
            pdu = struct.pack(fmt, function_code, starting_address, output_value)
            if not data_format:
                data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + value1+value2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.WRITE_MULTIPLE_COILS:
            byte_count = len(output_value) // 8
            if (len(output_value) % 8) > 0:
                byte_count += 1
            pdu = struct.pack(">BHHB", function_code, starting_address, len(output_value), byte_count)
            i, byte_value = 0, 0
            for j in output_value:
                if j > 0:
                    byte_value += pow(2, i)
                if i == 7:
                    pdu += struct.pack(">B", byte_value)
                    i, byte_value = 0, 0
                else:
                    i += 1
            if i > 0:
                pdu += struct.pack(">B", byte_value)
            if not data_format:
                data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.WRITE_MULTIPLE_REGISTERS:
            if output_value and data_format:
                byte_count =  struct.calcsize(data_format)
            else:
                byte_count = 2 * len(output_value)
            pdu = struct.pack(">BHHB", function_code, starting_address, byte_count / 2, byte_count)
            if output_value and data_format:
                pdu += struct.pack(data_format, *output_value)
            else:
                for j in output_value:
                    fmt = "H" if j >= 0 else "h"
                    pdu += struct.pack(">" + fmt, j)
            # data_format is now used to process response which is always 2 registers:
            #   1) data address of first register, 2) number of registers written
            data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.READ_EXCEPTION_STATUS:
            pdu = struct.pack(">B", function_code)
            data_format = ">B"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                expected_length = 5

        elif function_code == defines.DIAGNOSTIC:
            # SubFuncCode  are in starting_address
            pdu = struct.pack(">BH", function_code, starting_address)
            if len(output_value) > 0:
                for j in output_value:
                    # copy data in pdu
                    pdu += struct.pack(">B", j)
                if not data_format:
                    data_format = ">" + (len(output_value) * "B")
                if expected_length < 0:
                    # No length was specified and calculated length can be used:
                    # slave + func + SubFunc1 + SubFunc2 + Data + crc1 + crc2
                    expected_length = len(output_value) + 6

        elif function_code == defines.READ_WRITE_MULTIPLE_REGISTERS:
            is_read_function = True
            byte_count = 2 * len(output_value)
            pdu = struct.pack(
                ">BHHHHB",
                function_code, starting_address, quantity_of_x, defines.READ_WRITE_MULTIPLE_REGISTERS,
                len(output_value), byte_count
            )
            for j in output_value:
                fmt = "H" if j >= 0 else "h"
                # copy data in pdu
                pdu += struct.pack(">"+fmt, j)
            if not data_format:
                data_format = ">" + (quantity_of_x * "H")
            if expected_length < 0:
                # No lenght was specified and calculated length can be used:
                # slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2
                expected_length = 2 * quantity_of_x + 5
        else:
            raise ModbusFunctionNotSupportedError("The {0} function code is not supported. ".format(function_code))

        # instantiate a query which implements the MAC (TCP or RTU) part of the protocol
        query = self._make_query()

        # add the mac part of the protocol to the request
        request = query.build_request(pdu, slave)

        # send the request to the slave
        retval = call_hooks("modbus.Master.before_send", (self, request))
        if retval is not None:
            request = retval
        if self._verbose:
            LOGGER.debug(get_log_buffer("-> ", request))
        self._send(request)

        call_hooks("modbus.Master.after_send", (self, ))

        if slave != 0:
            # receive the data from the slave
            response = self._recv(expected_length)
            retval = call_hooks("modbus.Master.after_recv", (self, response))
            if retval is not None:
                response = retval
            if self._verbose:
                LOGGER.debug(get_log_buffer("<- ", response))

            # extract the pdu part of the response
            response_pdu = query.parse_response(response)

            # analyze the received data
            (return_code, byte_2) = struct.unpack(">BB", response_pdu[0:2])

            if return_code > 0x80:
                # the slave has returned an error
                exception_code = byte_2
                raise ModbusError(exception_code)
            else:
                if is_read_function:
                    # get the values returned by the reading function
                    byte_count = byte_2
                    data = response_pdu[2:]
                    if byte_count != len(data):
                        # the byte count in the pdu is invalid
                        raise ModbusInvalidResponseError(
                            "Byte count is {0} while actual number of bytes is {1}. ".format(byte_count, len(data))
                        )
                else:
                    # returns what is returned by the slave after a writing function
                    data = response_pdu[1:]

                # returns the data as a tuple according to the data_format
                # (calculated based on the function or user-defined)
                result = struct.unpack(data_format, data)
                if nb_of_digits > 0:
                    digits = []
                    for byte_val in result:
                        for i in range(8):
                            if len(digits) >= nb_of_digits:
                                break
                            digits.append(byte_val % 2)
                            byte_val = byte_val >> 1
                    result = tuple(digits)
                return result
Пример #10
0
    def execute(
        self, slave, function_code, starting_address, quantity_of_x=0, output_value=0, data_format="",
        expected_length=-1, write_starting_address_fc23=0, number_file=None, pdu=""
    ):
        """
        Execute a modbus query and returns the data part of the answer as a tuple
        The returned tuple depends on the query function code. see modbus protocol
        specification for details
        data_format makes possible to extract the data like defined in the
        struct python module documentation
        For function Read_File_Record 
        starting_address, quantity_of_x, number_file must be tuple () 
        of one long (by the number of requested sub_seq)
        the result will be 
        ((sub _ seq_0 _ data), (sub_seq_1_data),... (sub_seq_N_data)).
        """

        is_read_function = False
        nb_of_digits = 0
        if number_file is None:
            number_file = tuple()

        # open the connection if it is not already done
        self.open()

        # Build the modbus pdu and the format of the expected data.
        # It depends of function code. see modbus specifications for details.
        if function_code == defines.READ_COILS or function_code == defines.READ_DISCRETE_INPUTS:
            is_read_function = True
            pdu = struct.pack(">BHH", function_code, starting_address, quantity_of_x)
            byte_count = quantity_of_x // 8
            if (quantity_of_x % 8) > 0:
                byte_count += 1
            nb_of_digits = quantity_of_x
            if not data_format:
                data_format = ">" + (byte_count * "B")
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + bytcodeLen + bytecode + crc1 + crc2
                expected_length = byte_count + 5

        elif function_code == defines.READ_INPUT_REGISTERS or function_code == defines.READ_HOLDING_REGISTERS:
            is_read_function = True
            pdu = struct.pack(">BHH", function_code, starting_address, quantity_of_x)
            if not data_format:
                data_format = ">" + (quantity_of_x * "H")
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2
                expected_length = 2 * quantity_of_x + 5

        elif function_code == defines.READ_FILE_RECORD:
            is_read_function = True 
            if (
                isinstance(number_file, tuple)
                and isinstance(starting_address, tuple)
                and isinstance(quantity_of_x, tuple)
                and len(number_file) == len(starting_address) == len(quantity_of_x) > 0
            ):
                count_seq = len(number_file)
            else:
                raise ModbusInvalidRequestError(
                    'For function READ_FILE_RECORD param'
                    'starting_address, quantity_of_x, number_file must be tuple()'
                    'of one length > 0 (by the number of requested sub_seq)'
                )
            pdu = struct.pack(">BB", function_code, count_seq * 7) + b''.join(map(lambda zip_param: struct.pack(">BHHH", *zip_param), zip(count_seq * (6, ), number_file, starting_address, quantity_of_x)))
            if not data_format:
                data_format = ">BB" + 'BB'.join(map(lambda x: x*'H', quantity_of_x))
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + bytcodeLen + (byteLenSubReq+byteref+bytecode[] x 2)*countSubReq + crc1 + crc2
                expected_length = 2 * sum(quantity_of_x) + 2 * count_seq + 5

        elif (function_code == defines.WRITE_SINGLE_COIL) or (function_code == defines.WRITE_SINGLE_REGISTER):
            if function_code == defines.WRITE_SINGLE_COIL:
                if output_value != 0:
                    output_value = 0xff00
                fmt = ">BHH"
            else:
                fmt = ">BH"+("H" if output_value >= 0 else "h")
            pdu = struct.pack(fmt, function_code, starting_address, output_value)
            if not data_format:
                data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + value1+value2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.WRITE_MULTIPLE_COILS:
            byte_count = len(output_value) // 8
            if (len(output_value) % 8) > 0:
                byte_count += 1
            pdu = struct.pack(">BHHB", function_code, starting_address, len(output_value), byte_count)
            i, byte_value = 0, 0
            for j in output_value:
                if j > 0:
                    byte_value += pow(2, i)
                if i == 7:
                    pdu += struct.pack(">B", byte_value)
                    i, byte_value = 0, 0
                else:
                    i += 1
            if i > 0:
                pdu += struct.pack(">B", byte_value)
            if not data_format:
                data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.WRITE_MULTIPLE_REGISTERS:
            if output_value and data_format:
                byte_count =  struct.calcsize(data_format)
            else:
                byte_count = 2 * len(output_value)
            pdu = struct.pack(">BHHB", function_code, starting_address, byte_count // 2, byte_count)
            if output_value and data_format:
                pdu += struct.pack(data_format, *output_value)
            else:
                for j in output_value:
                    fmt = "H" if j >= 0 else "h"
                    pdu += struct.pack(">" + fmt, j)
            # data_format is now used to process response which is always 2 registers:
            #   1) data address of first register, 2) number of registers written
            data_format = ">HH"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2
                expected_length = 8

        elif function_code == defines.READ_EXCEPTION_STATUS:
            pdu = struct.pack(">B", function_code)
            data_format = ">B"
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                expected_length = 5

        elif function_code == defines.DIAGNOSTIC:
            # SubFuncCode  are in starting_address
            pdu = struct.pack(">BH", function_code, starting_address)
            if len(output_value) > 0:
                for j in output_value:
                    # copy data in pdu
                    pdu += struct.pack(">B", j)
                if not data_format:
                    data_format = ">" + (len(output_value) * "B")
                if expected_length < 0:
                    # No length was specified and calculated length can be used:
                    # slave + func + SubFunc1 + SubFunc2 + Data + crc1 + crc2
                    expected_length = len(output_value) + 6

        elif function_code == defines.READ_WRITE_MULTIPLE_REGISTERS:
            is_read_function = True
            byte_count = 2 * len(output_value)
            pdu = struct.pack(
                ">BHHHHB",
                function_code, starting_address, quantity_of_x, write_starting_address_fc23,
                len(output_value), byte_count
            )
            for j in output_value:
                fmt = "H" if j >= 0 else "h"
                # copy data in pdu
                pdu += struct.pack(">"+fmt, j)
            if not data_format:
                data_format = ">" + (quantity_of_x * "H")
            if expected_length < 0:
                # No length was specified and calculated length can be used:
                # slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2
                expected_length = 2 * quantity_of_x + 5

        elif function_code == defines.RAW:
            # caller has to set arguments "pdu", "expected_length", and "data_format"
            pass	

        elif function_code == defines.DEVICE_INFO:
            # is_read_function = True
            mei_type = 0x0E
            pdu = struct.pack(
                ">BBBB",
                # function_code = 43 (0x2B)
                # MEI Type = 0x0E (Read Device Identification)
                # output_value[0] = Read Device ID code
                # output_value[1] = Object Id
                function_code, mei_type, output_value[0], output_value[1]
            )

        else:
            raise ModbusFunctionNotSupportedError("The {0} function code is not supported. ".format(function_code))

        # instantiate a query which implements the MAC (TCP or RTU) part of the protocol
        query = self._make_query()

        # add the mac part of the protocol to the request
        request = query.build_request(pdu, slave)

        # send the request to the slave
        retval = call_hooks("modbus.Master.before_send", (self, request))
        if retval is not None:
            request = retval
        if self._verbose:
            LOGGER.debug(get_log_buffer("-> ", request))
        self._send(request)

        call_hooks("modbus.Master.after_send", (self, ))

        if slave != 0:
            # receive the data from the slave
            response = self._recv(expected_length)
            retval = call_hooks("modbus.Master.after_recv", (self, response))
            if retval is not None:
                response = retval
            if self._verbose:
                LOGGER.debug(get_log_buffer("<- ", response))

            # extract the pdu part of the response
            response_pdu = query.parse_response(response)

            # analyze the received data
            (return_code, byte_2) = struct.unpack(">BB", response_pdu[0:2])

            if return_code > 0x80:
                # the slave has returned an error
                exception_code = byte_2
                raise ModbusError(exception_code)
            else:
                if is_read_function:
                    # get the values returned by the reading function
                    byte_count = byte_2
                    data = response_pdu[2:]
                    if byte_count != len(data):
                        # the byte count in the pdu is invalid
                        raise ModbusInvalidResponseError(
                            "Byte count is {0} while actual number of bytes is {1}. ".format(byte_count, len(data))
                        )
                elif function_code == defines.DEVICE_INFO:
                    data = response_pdu[1:]
                    data_format = ">" + (len(data) * "B")
                else:
                    # returns what is returned by the slave after a writing function
                    data = response_pdu[1:]

                # returns the data as a tuple according to the data_format
                # (calculated based on the function or user-defined)
                if (re.match("[>]?[sp]?",data_format)):
                    result = data.decode()
                else:
                    result = struct.unpack(data_format, data)
                if nb_of_digits > 0:
                    digits = []
                    for byte_val in result:
                        for i in range(8):
                            if len(digits) >= nb_of_digits:
                                break
                            digits.append(byte_val % 2)
                            byte_val = byte_val >> 1
                    result = tuple(digits)
                if function_code == defines.READ_FILE_RECORD:
                    sub_seq = list()
                    ptr = 0
                    while ptr < len(result):
                        sub_seq += ((ptr + 2, ptr + 2 + result[ptr] // 2), )
                        ptr += result[ptr] // 2 + 2
                    result = tuple(map(lambda sub_seq_x: result[sub_seq_x[0]:sub_seq_x[1]], sub_seq))
                return result