Response length is invalid 0

427 views
Skip to first unread message

Nissim Derdiger

unread,
Jan 5, 2021, 3:20:03 PM1/5/21
to modbus-tk
Hi,
I'm trying to implement both the TCP and RTU masters using your lib.
The TCP works very good without issues.
However, the RTU part keeps returning this message: "Response length is invalid 0"
i've tripled check that there is nothing wrong with the COM port, with the adapter or with the specific address using other simulator, and everything work from there.

So, this is what i did - please tell me if you see what i did wrong..

thanks!!
Nissim.

try:
    from modbus_tk import modbus_rtu as ModbusMaster
    import modbus_tk.defines as cst
except Exception as e:
    print('modbus_tk is not installed!')
    raise ('modbus_tk Is not installed!' + e.args[0])
try:
    # since we use Python 3.6 - this is not really the serial lib (which is not supported anymore)
    # but a lib of pyserial that requires installation
    import serial
except Exception as e:
    print('PYSERIAL Is not installed!')
    raise ('PYSERIAL Is not installed!' + e.args[0])
import DLLs.EnumClass as EnumClass
import numpy as np
import enum


class SupportedFormats(enum.Enum):
    f8 = 1
    f4 = 2
    u1 = 3
    u2 = 4
    u4 = 5
    i1 = 6
    i2 = 7
    i4 = 8
    str = 9


class Endianess(enum.Enum):
    big = 1
    little = 2


class ModbusMessages(enum.Enum):
    Connection_Succeeded = 1
    Connection_Failed = 2
    General_Error = 3
    Invalid_Function_Code = 4
    Unable_To_Read_Data = 5
    Parameter_Format_Not_Supported = 6
    OK = 7
    One_Of_The_Values_Does_Not_Match_The_Parameter_Type = 8
    Registers_Number_Does_Not_Match_The_Parameter_Type = 9
    Illegal_Function = 10
    Illegal_Data_Address = 11
    Illegal_Data_Value = 12
    Slave_Device_Failure = 13
    Acknowledge = 14
    Slave_Device_Busy = 15
    Negative_Acknowledge = 16
    Memory_Parity_Error = 17
    Gateway_Path_Unavailable = 18
    Gateway_Target_Device_Failed_To_Respond = 19
    Unknown_Exception_ID = 20


class ModbusRTUMaster:

    def __init__(self, com, boudrate, bytesize, parity, stopbits, slave_id=159,
                 endianess=Endianess.big, timeout_in_sec=1):
        import serial
        self.slave_id = slave_id
        self.endian = '>'
        if endianess == Endianess.little:
            self.endian = '<'
        np.set_printoptions(suppress=True)
        self.modbus = ModbusMaster.RtuMaster(serial.Serial(port=com, baudrate=boudrate, bytesize=bytesize.value,
                                                           parity=parity.name, stopbits=stopbits,
                                                           timeout=timeout_in_sec))

    def disconnect(self):
        self.modbus.close()

    def read(self, start_address, num_of_registers, function_code, parameter_format):
        """
        :param start_address: positive integer, the address without offset
        :param num_of_registers: positive integer, how many register total should we ask for in the request
        :param function_code: can only be 3 or 4 (as a number)
        :param parameter_format: float, double, integer (8,16 or 32), uint(8,16 or 32) or string
        :return:
        """
        # get the raw data
        # Payload = None
        if function_code not in [3, 4]:
            raise ModbusMessages.Invalid_Function_Code
        try:
            payload = self.modbus.execute(slave=self.slave_id, function_code=function_code,
                                          starting_address=start_address,
                                          quantity_of_x=num_of_registers)  # , data_format='>HH')
        except Exception as e:
            raise 'Modbus TCP returned Unhandled Exception: ' + e.args[0]
        # if 3 times of 3 retries 3 seconds each weren't enough
        if payload is None:
            return ModbusMessages.Unable_To_Read_Data, np.array([])
        result = np.array(payload)
        # this modbus library always returns int32. So if the registers are 16bit - we need to remove
        # the upper 16 bits from each one and unite them to 32bit registers
        # (also replacing the python list with np arrays for speed and performance.
        try:
            result = np.array(result, dtype='>u2')
            # group the register together so each one will be in the same size as the parameter you want
            # your response to be in.
            if parameter_format.name == 'str':
                result = np.frombuffer(result.data, dtype='c')
                response_string = ''
                for c in result:
                    try:
                        response_string += c.decode('UTF-8')
                    except ValueError:
                        response_string += ' '
                return ModbusMessages.OK, response_string
            if '4' in parameter_format.name:
                result = np.frombuffer(result.data, dtype='>u4')
                result = np.array(result, dtype='<u4')
            elif '8' in parameter_format.name:
                result = np.frombuffer(result.data, dtype='>u8')
                result = np.array(result, dtype='<u8')
            elif '1' in parameter_format.name:
                result = np.frombuffer(result.data, dtype='>u2')
                result = np.array(result, dtype='<u2')
            else:
                result = np.array(result, dtype='<u2')
            # cast the values to the requested Parameter Format
            result = np.frombuffer(result, dtype='<' + parameter_format.name)
            return result
        except Exception as e:
            if 'could not convert string to float:' in e.args[0]:
                status = ModbusMessages.One_Of_The_Values_Does_Not_Match_The_Parameter_Type
            elif 'buffer size must be a multiple of element size' in e.args[0]:
                status = ModbusMessages.Registers_Number_Does_Not_Match_The_Parameter_Type
            else:
                status = 'Modbus TCP returned Unhandled Exception: ' + e.args[0]
            raise status

    def write(self, start_address, num_of_registers, values, parameter_format):
        if parameter_format not in SupportedFormats:
            return ModbusMessages.Parameter_Format_Not_Supported
        # ready the data (make a uint16 list out of it)
        try:
            ints = []
            if ('4' in parameter_format.name) | ('8' in parameter_format.name):  # uint32, int32, float and double
                ints = np.array(values, dtype='>' + parameter_format.name)
                ints = np.frombuffer(ints.data, dtype='>u2')
            elif 'i' in parameter_format.name:  # int16 and int8
                ints = np.array(values, dtype='>u2')
                ints = np.frombuffer(ints.data, dtype='>u2')
            elif parameter_format.name == 'str':  # string
                values = list(values)
                ints = np.array(values, dtype='c')
                ints = np.frombuffer(ints.data, dtype='>u2')
                ints = np.append(ints, np.array(np.zeros(num_of_registers - len(ints)), dtype='>u2'))
            else:  # uint16 and uint8
                ints = np.array(values, dtype='>u2')
        except Exception as e:
            if 'could not convert string to float:' in e.args[0]:
                status = ModbusMessages.One_Of_The_Values_Does_Not_Match_The_Parameter_Type
            elif 'buffer size must be a multiple of element size' in e.args[0]:
                status = ModbusMessages.Registers_Number_Does_Not_Match_The_Parameter_Type
            else:
                status = 'Modbus TCP returned Unhandled Exception: ' + e.args[0]
            raise status
        # send the data
        try:
            ints = ints.tolist()
            resp = self.modbus.execute(self.slave_id, cst.WRITE_MULTIPLE_REGISTERS, start_address, output_value=ints)
            return ModbusMessages.OK
        except Exception as e:
            print(e.args[0])
            details = e.args[0]
            if 'Exception code =' in details:
                exception_code = details[(details.index('=') + 1):]
                details = exceptions_translator(int(exception_code))
            raise details


# ExceptionsTranslator
def exceptions_translator(exception_id):
    if exception_id == 1:
        return ModbusMessages.Illegal_Function
    elif exception_id == 2:
        return ModbusMessages.Illegal_Data_Address
    elif exception_id == 3:
        return ModbusMessages.Illegal_Data_Value
    elif exception_id == 4:
        return ModbusMessages.Slave_Device_Failure
    elif exception_id == 5:
        return ModbusMessages.Acknowledge
    elif exception_id == 6:
        return ModbusMessages.Slave_Device_Busy
    elif exception_id == 7:
        return ModbusMessages.Negative_Acknowledge
    elif exception_id == 8:
        return ModbusMessages.Memory_Parity_Error
    elif exception_id == 10:
        return ModbusMessages.Gateway_Path_Unavailable
    elif exception_id == 11:
        return ModbusMessages.Gateway_Target_Device_Failed_To_Respond
    else:
        return ModbusMessages.Unknown_Exception_ID



Nissim Derdiger

unread,
Jan 5, 2021, 4:21:24 PM1/5/21
to modbus-tk
Hi again,
Found my problem - I've forgot to add this line:
master.set_verbose(True) 
that's why it did not work.
please delete this thread.

ב-יום שלישי, 5 בינואר 2021 בשעה 22:20:03 UTC+2, ‪Nissim Derdiger‬‏ כתב/ה:
Reply all
Reply to author
Forward
0 new messages