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..
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,
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.
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
result = np.frombuffer(result.data, dtype='>u4')
result = np.array(result, dtype='<u4')
result = np.frombuffer(result.data, dtype='>u8')
result = np.array(result, dtype='<u8')
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
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 = []
ints = np.frombuffer(ints.data, dtype='>u2')
ints = np.array(values, dtype='>u2')
ints = np.frombuffer(ints.data, dtype='>u2')
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