add tcp support to modbus component tbt
This commit is contained in:
parent
506587283c
commit
ef4e98cf6b
|
|
@ -26,8 +26,12 @@ poll_time: 10
|
|||
hold_time: 10
|
||||
|
||||
[tecna_t3]
|
||||
port: /dev/ttyUSB0
|
||||
model: t3p
|
||||
model = t3l
|
||||
method = tcp
|
||||
host = 10.10.10.3
|
||||
port = 502
|
||||
unit = 1
|
||||
timeout = 3
|
||||
|
||||
[fixture_rfid]
|
||||
port: USB1
|
||||
|
|
|
|||
|
|
@ -3,24 +3,26 @@ import traceback
|
|||
import warnings
|
||||
|
||||
import pymodbus
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
# Conditionally import real or dummy serial library
|
||||
if "--sim-serial" in sys.argv:
|
||||
from components.dummies.serial import serial
|
||||
else:
|
||||
import serial
|
||||
|
||||
from pymodbus.constants import Endian
|
||||
# from pymodbus.exceptions import ModbusIOException
|
||||
from pymodbus.payload import BinaryPayloadBuilder, BinaryPayloadDecoder
|
||||
|
||||
# Conditionally import real or dummy Modbus clients
|
||||
if "--sim-modbus" not in sys.argv:
|
||||
from pymodbus.client import ModbusSerialClient as ModbusClient
|
||||
from pymodbus.client import ModbusSerialClient
|
||||
from pymodbus.client import ModbusTcpClient
|
||||
else:
|
||||
from components.dummies.pymodbus import ModbusClient
|
||||
from components.dummies.pymodbus import ModbusSerialClient
|
||||
# Assuming a dummy TCP client exists for simulation purposes
|
||||
from components.dummies.pymodbus import ModbusTcpClient
|
||||
|
||||
from PyQt5.QtCore import QMutex, pyqtSignal
|
||||
|
||||
from .component import Component
|
||||
|
||||
|
||||
|
|
@ -31,25 +33,34 @@ class ModbusComponent(Component):
|
|||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
||||
self.registers = registers if registers is not None else {}
|
||||
self.lock = QMutex()
|
||||
self.client = None
|
||||
self.unit = 1
|
||||
|
||||
def config_changed(self):
|
||||
|
||||
# Read configuration values
|
||||
# Read shared and distinguishing configuration values
|
||||
self.method = self.config[self.name].get("method", "rtu").lower()
|
||||
self.timeout = int(self.config[self.name].get("timeout", 1))
|
||||
self.unit = int(self.config[self.name].get("unit", 1)) # Slave ID
|
||||
self.byteorder = getattr(Endian, self.config[self.name].get("byteorder", "Big").upper())
|
||||
self.wordorder = getattr(Endian, self.config[self.name].get("wordorder", "Little").upper())
|
||||
|
||||
# Lock to ensure thread safety during re-initialization
|
||||
self.lock.lock()
|
||||
try:
|
||||
# Close existing client connection if it exists
|
||||
if self.client and self.client.is_socket_open():
|
||||
self.client.close()
|
||||
|
||||
# Initialize the appropriate Modbus client based on the method
|
||||
if self.method == 'rtu':
|
||||
# Serial (RTU) specific configuration
|
||||
self.port = self.config[self.name]["port"]
|
||||
self.baudrate = int(self.config[self.name]["baudrate"])
|
||||
self.stopbits = getattr(serial, self.config[self.name].get("stopbits", "stopbits_one").upper())
|
||||
self.parity = getattr(serial, self.config[self.name].get("parity", "parity_none").upper())
|
||||
self.bytesize = getattr(serial, self.config[self.name].get("bytesize", "eightbits").upper())
|
||||
self.byteorder = getattr(Endian, self.config[self.name].get("byteorder", "Big").upper())
|
||||
self.wordorder = getattr(Endian, self.config[self.name].get("wordorder", "Little").upper())
|
||||
self.timeout = int(self.config[self.name].get("timeout", 1))
|
||||
|
||||
# Lock the interaction to ensure thread safety
|
||||
self.lock.lock()
|
||||
try:
|
||||
# Initialize the Modbus client
|
||||
self.client = ModbusClient(
|
||||
self.client = ModbusSerialClient(
|
||||
method=self.method,
|
||||
port=self.port,
|
||||
stopbits=self.stopbits,
|
||||
|
|
@ -59,19 +70,33 @@ class ModbusComponent(Component):
|
|||
timeout=self.timeout,
|
||||
strict=False,
|
||||
)
|
||||
connection_details = f"port {self.port}"
|
||||
elif self.method == 'tcp':
|
||||
# Ethernet (TCP) specific configuration
|
||||
self.host = self.config[self.name]["host"]
|
||||
self.port = int(self.config[self.name].get("port", 502))
|
||||
|
||||
self.client = ModbusTcpClient(
|
||||
host=self.host,
|
||||
port=self.port,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
connection_details = f"host {self.host}:{self.port}"
|
||||
else:
|
||||
raise NotImplementedError(f"Modbus method '{self.method}' is not supported.")
|
||||
|
||||
# Connect and verify the connection
|
||||
if not self.client.connect():
|
||||
raise ConnectionError(f"Cannot connect to Modbus on port {self.port}")
|
||||
raise ConnectionError(f"Cannot connect to Modbus slave on {connection_details}")
|
||||
|
||||
if not self.client.is_socket_open():
|
||||
raise ConnectionError(f"Connection socket not open on port {self.port}")
|
||||
raise ConnectionError(f"Connection socket not open for {connection_details}")
|
||||
|
||||
except FileNotFoundError as e:
|
||||
error_message = f"Serial port error: {self.port} not found. Check your configuration."
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
except (FileNotFoundError, ConnectionRefusedError):
|
||||
error_message = f"Modbus connection error: {connection_details} not found or connection refused. Check configuration and device status."
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
except Exception as e:
|
||||
error_message = f"Configuration error: {str(e)}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
finally:
|
||||
self.lock.unlock()
|
||||
|
|
@ -80,20 +105,21 @@ class ModbusComponent(Component):
|
|||
"""Read holding registers with error handling."""
|
||||
self.lock.lock()
|
||||
try:
|
||||
# Set the default slave unit ID for the transaction if not provided
|
||||
kwargs.setdefault('unit', self.unit)
|
||||
read = self.client.read_holding_registers(register, count=count, **kwargs)
|
||||
if read.isError():
|
||||
error_message = f"Modbus read failed: Could not read Modbus register {register}"
|
||||
error_message = f"Modbus read failed: Could not read register {register} from unit {kwargs['unit']}"
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
raise ValueError(f"Modbus read error at register {register}")
|
||||
return read
|
||||
except pymodbus.exceptions.ConnectionException:
|
||||
error_message = f"Modbus read failed: Connection error at port {self.port}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
connection_details = self.port if self.method == 'rtu' else self.host
|
||||
error_message = f"Modbus read failed: Connection error at {connection_details}"
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
return None # Return None to signal failure
|
||||
except Exception as e:
|
||||
error_message = f"Error reading Modbus register {register}: {str(e)}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
return None
|
||||
finally:
|
||||
|
|
@ -103,19 +129,19 @@ class ModbusComponent(Component):
|
|||
"""Write to holding registers with error handling."""
|
||||
self.lock.lock()
|
||||
try:
|
||||
# Set the default slave unit ID for the transaction if not provided
|
||||
kwargs.setdefault('unit', self.unit)
|
||||
wrote = self.client.write_registers(register, value, skip_encode=True, **kwargs)
|
||||
|
||||
# Check if the response indicates an error
|
||||
if wrote.isError():
|
||||
raise ValueError(f"Modbus write error at register {register}")
|
||||
raise ValueError(f"Modbus write error at register {register} on unit {kwargs['unit']}")
|
||||
return wrote
|
||||
except pymodbus.exceptions.ConnectionException as ce:
|
||||
error_message = f"Modbus write failed: Connection error at port {self.port}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
except pymodbus.exceptions.ConnectionException:
|
||||
connection_details = self.port if self.method == 'rtu' else self.host
|
||||
error_message = f"Modbus write failed: Connection error at {connection_details}"
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
except Exception as e:
|
||||
error_message = f"Error writing Modbus register {register} with value {value}: {str(e)}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
finally:
|
||||
self.lock.unlock()
|
||||
|
|
@ -124,7 +150,6 @@ class ModbusComponent(Component):
|
|||
"""Decode data safely."""
|
||||
if read is None:
|
||||
error_message = "Error decoding Modbus data: No data to decode (read returned None)"
|
||||
#self.log.error(error_message)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
return None
|
||||
try:
|
||||
|
|
@ -136,11 +161,9 @@ class ModbusComponent(Component):
|
|||
return int(abs(data)) if "uint" in data_type else int(data)
|
||||
except AttributeError:
|
||||
error_message = "Modbus read returned invalid data (NoneType encountered)"
|
||||
#self.log.error(error_message)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
except Exception as e:
|
||||
error_message = f"Error decoding Modbus data: {str(e)}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
return None
|
||||
|
||||
|
|
@ -159,7 +182,6 @@ class ModbusComponent(Component):
|
|||
return builder.build()
|
||||
except Exception as e:
|
||||
error_message = f"Error encoding Modbus data: {str(e)}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
return None
|
||||
|
||||
|
|
@ -181,7 +203,6 @@ class ModbusComponent(Component):
|
|||
)
|
||||
except Exception as e:
|
||||
error_message = f"Error inside Modbus read: {str(e)}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
return None
|
||||
|
||||
|
|
@ -193,16 +214,15 @@ class ModbusComponent(Component):
|
|||
self._write(register, encoded_data, **kwargs)
|
||||
except Exception as e:
|
||||
error_message = f"Error inside Modbus write: {str(e)}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
|
||||
def __del__(self, event=None):
|
||||
try:
|
||||
self.lock.lock()
|
||||
if self.client.is_socket_open():
|
||||
if self.client and self.client.is_socket_open():
|
||||
self.client.close()
|
||||
except Exception as e:
|
||||
error_message = f"Error during Modbus cleanup: {str(e)}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
# self.log.error(error_message, exc_info=True)
|
||||
finally:
|
||||
self.lock.unlock()
|
||||
Loading…
Reference in New Issue
Block a user