From dc2816fec14278175413c01a8ae09bc7fb95afe5 Mon Sep 17 00:00:00 2001 From: MAKOMO Date: Sun, 15 Sep 2024 12:58:24 +0200 Subject: [PATCH] - add "strict" flag to serial MODBUS setup allowing to reset inter_byte_timeout on connecting via serial MODBUS if the flag is disabled - updates Trinitas setups --- src/artisanlib/main.py | 6 +++++- src/artisanlib/modbusport.py | 12 +++++++++--- src/artisanlib/ports.py | 9 +++++++++ src/includes/Machines/TRINITAS/T2.aset | 1 + src/includes/Machines/TRINITAS/T2_air.aset | 1 + src/includes/Machines/TRINITAS/T2_legacy.aset | 1 + src/includes/Machines/TRINITAS/T7.aset | 1 + src/includes/Machines/TRINITAS/T7_gas.aset | 1 + src/includes/Machines/TRINITAS/T7_legacy.aset | 1 + 9 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/artisanlib/main.py b/src/artisanlib/main.py index 1ec67954c..647cebb86 100644 --- a/src/artisanlib/main.py +++ b/src/artisanlib/main.py @@ -4173,6 +4173,7 @@ def blockTicks(self) -> int: def setSamplingRate(self, rate:int) -> None: self.qmc.delay = max(self.qmc.min_delay, rate) self.sampling_ticks_to_block_quantifiction = self.blockTicks() # we update the quantification block ticks + _log.info('setSamplingRate(%s)', self.qmc.delay) @pyqtSlot() def updateMessageLog(self) -> None: @@ -17110,6 +17111,7 @@ def settingsLoad(self, filename:Optional[str] = None, theme:bool = False, machin self.modbus.stopbits = toInt(settings.value('stopbits',self.modbus.stopbits)) self.modbus.parity = s2a(toString(settings.value('parity',self.modbus.parity))) self.modbus.timeout = max(0.3, float2float(toFloat(settings.value('timeout',self.modbus.timeout)))) # min serial MODBUS timeout is 300ms + self.modbus.serial_strict_timing = bool(toBool(settings.value('serial_strict_timing',self.modbus.serial_strict_timing))) self.modbus.modbus_serial_extra_read_delay = toFloat(settings.value('modbus_serial_extra_read_delay',self.modbus.modbus_serial_extra_read_delay)) self.modbus.serial_readRetries = toInt(settings.value('serial_readRetries',self.modbus.serial_readRetries)) self.modbus.IP_timeout = float2float(toFloat(settings.value('IP_timeout',self.modbus.IP_timeout))) @@ -18835,6 +18837,7 @@ def saveAllSettings(self, settings:QSettings, default_settings:Optional[Dict[str self.settingsSetValue(settings, default_settings, 'stopbits',self.modbus.stopbits, read_defaults) self.settingsSetValue(settings, default_settings, 'parity',self.modbus.parity, read_defaults) self.settingsSetValue(settings, default_settings, 'timeout',self.modbus.timeout, read_defaults) + self.settingsSetValue(settings, default_settings, 'serial_strict_timing',self.modbus.serial_strict_timing, read_defaults) self.settingsSetValue(settings, default_settings, 'modbus_serial_extra_read_delay',self.modbus.modbus_serial_extra_read_delay, read_defaults) self.settingsSetValue(settings, default_settings, 'serial_readRetries',self.modbus.serial_readRetries, read_defaults) self.settingsSetValue(settings, default_settings, 'IP_timeout',self.modbus.IP_timeout, read_defaults) @@ -22816,6 +22819,7 @@ def setcommport(self, _:bool = False) -> None: self.modbus.IP_timeout = float2float(toFloat(str(dialog.modbus_IP_timeoutEdit.text()))) except Exception: # pylint: disable=broad-except pass + self.modbus.serial_strict_timing = bool(dialog.modbus_Serial_strict.isChecked()) self.modbus.IP_retries = dialog.modbus_IP_retriesComboBox.currentIndex() self.modbus.PID_slave_ID = int(str(dialog.modbus_PIDslave_Edit.text())) self.modbus.PID_SV_register = int(str(dialog.modbus_SVregister_Edit.text())) @@ -25853,7 +25857,7 @@ def initialize_locale(my_app:Artisan) -> str: qt_translation_modules:List[str] = [ 'qtbase', 'qtconnectivity', -# 'qtwebengine' +# 'qtwebengine' # we do not use any UI ] # NOTE: on updates, need to update util.py:locale2full_local() as well diff --git a/src/artisanlib/modbusport.py b/src/artisanlib/modbusport.py index 359b431e2..dbde2a013 100644 --- a/src/artisanlib/modbusport.py +++ b/src/artisanlib/modbusport.py @@ -18,6 +18,7 @@ import sys import time import logging +import serial from typing import Final, Optional, List, Dict, Tuple, Union, Any, Awaitable, TYPE_CHECKING if TYPE_CHECKING: @@ -100,7 +101,7 @@ def getBinaryPayloadDecoderFromRegisters(registers:List[int], byteorderLittle:bo class modbusport: """ this class handles the communications with all the modbus devices""" - __slots__ = [ 'aw', 'modbus_serial_read_delay', 'modbus_serial_extra_read_delay', 'modbus_serial_write_delay', 'maxCount', 'readRetries', 'default_comport', 'comport', 'baudrate', 'bytesize', 'parity', 'stopbits', + __slots__ = [ 'aw', 'modbus_serial_read_delay', 'modbus_serial_extra_read_delay', 'modbus_serial_write_delay', 'maxCount', 'readRetries', 'serial_strict_timing', 'default_comport', 'comport', 'baudrate', 'bytesize', 'parity', 'stopbits', 'timeout', 'IP_timeout', 'IP_retries', 'serial_readRetries', 'PID_slave_ID', 'PID_SV_register', 'PID_p_register', 'PID_i_register', 'PID_d_register', 'PID_ON_action', 'PID_OFF_action', 'channels', 'inputSlaves', 'inputRegisters', 'inputFloats', 'inputBCDs', 'inputFloatsAsInt', 'inputBCDsAsInt', 'inputSigned', 'inputCodes', 'inputDivs', 'inputModes', 'optimizer', 'fetch_max_blocks', 'fail_on_cache_miss', 'disconnect_on_error', 'acceptable_errors', 'reset_socket', 'activeRegisters', 'readingsCache', 'SVmultiplier', 'PIDmultiplier', @@ -124,6 +125,7 @@ def __init__(self, aw:'ApplicationWindow') -> None: self.stopbits:int = 1 self.timeout:float = 0.3 # serial MODBUS timeout self.serial_readRetries:int = 0 # user configurable, defaults to 0 + self.serial_strict_timing:bool = False self.IP_timeout:float = 0.2 # UDP/TCP MODBUS timeout in seconds self.IP_retries:int = 1 # UDP/TCP MODBUS retries (max 3) self.PID_slave_ID:int = 0 @@ -240,7 +242,7 @@ def formatMS(start:float, end:float) -> str: # @staticmethod # def reconnect() -> None: -# _log.info('reconnect()') +# _log.info('reonnect()') def connect(self) -> None: if not self.isConnected(): @@ -357,7 +359,7 @@ def connect(self) -> None: # retry_on_invalid=True, # retry on invalid response; by default False # retired # close_comm_on_error=self.reset_socket, # reset_socket=self.reset_socket, # retired -# strict=False, # settings this to False disables the inter char timeout restriction # retired +# strict=False, # settings this to False disables the inter char timeout restriction # retired and replaced by self.serial_strict_timing reconnect_delay=0, # avoid automatic reconnection # on_reconnect_callback=self.reconnect, # removed in pymodbus 3.7 timeout=min((self.aw.qmc.delay/2000), self.timeout)) # the timeout should not be larger than half of the sampling interval @@ -366,6 +368,10 @@ def connect(self) -> None: time.sleep(.2) # avoid possible hickups on startup if self.master is not None: self.master.connect() # type:ignore[no-untyped-call,unused-ignore] + # on connect() of serial connections we reset the inter_byte_timeout of the serial socket if serial_strict_timing is False + # by default pymodbus v3.7 sets a calculated inter_byte_timeout on connect() + if not self.serial_strict_timing and self.type in {0,1} and self.master.socket is not None and isinstance(self.master.socket, serial.Serial): + self.master.socket.inter_byte_timeout = None if self.isConnected(): self.updateActiveRegisters() self.clearReadingsCache() diff --git a/src/artisanlib/ports.py b/src/artisanlib/ports.py index cb9c7d295..142f7f39d 100644 --- a/src/artisanlib/ports.py +++ b/src/artisanlib/ports.py @@ -685,6 +685,7 @@ def __init__(self, parent:'QWidget', aw:'ApplicationWindow') -> None: self.modbus_Serial_delayEdit = QLineEdit(str(int(self.aw.modbus.modbus_serial_extra_read_delay*1000))) self.modbus_Serial_delayEdit.setValidator(self.aw.createCLocaleDoubleValidator(0,99,0,self.modbus_Serial_delayEdit)) self.modbus_Serial_delayEdit.setFixedWidth(50) + self.modbus_Serial_delayEdit.setAlignment(Qt.AlignmentFlag.AlignRight) self.modbus_Serial_delayEdit.setToolTip(QApplication.translate('Tooltip', 'Extra delay in Milliseconds between MODBUS Serial commands')) modbus_Serial_retries = QLabel(QApplication.translate('Label', 'Retries')) self.modbus_Serial_retriesComboBox = QComboBox() @@ -692,11 +693,18 @@ def __init__(self, parent:'QWidget', aw:'ApplicationWindow') -> None: self.modbus_Serial_retriesComboBox.addItems([str(n) for n in range(3)]) self.modbus_Serial_retriesComboBox.setCurrentIndex(self.aw.modbus.serial_readRetries) + modbus_Serial_strict_label = QLabel(QApplication.translate('Label', 'Strict')) + self.modbus_Serial_strict = QCheckBox() + self.modbus_Serial_strict.setChecked(self.aw.modbus.serial_strict_timing) + self.modbus_Serial_strict.setFocusPolicy(Qt.FocusPolicy.NoFocus) + modbus_Serial_grid = QGridLayout() modbus_Serial_grid.addWidget(modbus_Serial_delaylabel,0,0,Qt.AlignmentFlag.AlignRight) modbus_Serial_grid.addWidget(self.modbus_Serial_delayEdit,0,1,Qt.AlignmentFlag.AlignRight) modbus_Serial_grid.addWidget(modbus_Serial_retries,1,0,Qt.AlignmentFlag.AlignRight) modbus_Serial_grid.addWidget(self.modbus_Serial_retriesComboBox,1,1,Qt.AlignmentFlag.AlignRight) + modbus_Serial_grid.addWidget(modbus_Serial_strict_label,2,0,Qt.AlignmentFlag.AlignRight) + modbus_Serial_grid.addWidget(self.modbus_Serial_strict,2,1,Qt.AlignmentFlag.AlignLeft) modbus_Serial_layout = QVBoxLayout() modbus_Serial_layout.addLayout(modbus_Serial_grid) @@ -711,6 +719,7 @@ def __init__(self, parent:'QWidget', aw:'ApplicationWindow') -> None: self.modbus_IP_timeoutEdit = QLineEdit(str(self.aw.modbus.IP_timeout)) self.modbus_IP_timeoutEdit.setValidator(self.aw.createCLocaleDoubleValidator(0,5,1,self.modbus_IP_timeoutEdit)) self.modbus_IP_timeoutEdit.setFixedWidth(50) + self.modbus_IP_timeoutEdit.setAlignment(Qt.AlignmentFlag.AlignRight) self.modbus_IP_timeoutEdit.setToolTip(QApplication.translate('Tooltip', 'IP timeout in seconds, not larger than half of the sampling interval')) modbus_IP_retries = QLabel(QApplication.translate('Label', 'Retries')) self.modbus_IP_retriesComboBox = QComboBox() diff --git a/src/includes/Machines/TRINITAS/T2.aset b/src/includes/Machines/TRINITAS/T2.aset index eb42853c0..595b4d756 100644 --- a/src/includes/Machines/TRINITAS/T2.aset +++ b/src/includes/Machines/TRINITAS/T2.aset @@ -119,6 +119,7 @@ type=0 wordorderLittle=true optimizer=true fetch_max_blocks=false +serial_strict_timing=false [MachineSetup] capacity=2 diff --git a/src/includes/Machines/TRINITAS/T2_air.aset b/src/includes/Machines/TRINITAS/T2_air.aset index 82db43ce5..289b200f9 100644 --- a/src/includes/Machines/TRINITAS/T2_air.aset +++ b/src/includes/Machines/TRINITAS/T2_air.aset @@ -119,6 +119,7 @@ type=0 wordorderLittle=true optimizer=true fetch_max_blocks=false +serial_strict_timing=false [MachineSetup] capacity=2 diff --git a/src/includes/Machines/TRINITAS/T2_legacy.aset b/src/includes/Machines/TRINITAS/T2_legacy.aset index fa0e65098..4c876cd41 100644 --- a/src/includes/Machines/TRINITAS/T2_legacy.aset +++ b/src/includes/Machines/TRINITAS/T2_legacy.aset @@ -119,6 +119,7 @@ type=0 wordorderLittle=true optimizer=true fetch_max_blocks=false +serial_strict_timing=false [MachineSetup] capacity=2 diff --git a/src/includes/Machines/TRINITAS/T7.aset b/src/includes/Machines/TRINITAS/T7.aset index db847da77..b3d803c12 100644 --- a/src/includes/Machines/TRINITAS/T7.aset +++ b/src/includes/Machines/TRINITAS/T7.aset @@ -119,6 +119,7 @@ type=0 wordorderLittle=true optimizer=true fetch_max_blocks=false +serial_strict_timing=false [MachineSetup] capacity=7 diff --git a/src/includes/Machines/TRINITAS/T7_gas.aset b/src/includes/Machines/TRINITAS/T7_gas.aset index 73f8013a1..f56d97252 100644 --- a/src/includes/Machines/TRINITAS/T7_gas.aset +++ b/src/includes/Machines/TRINITAS/T7_gas.aset @@ -119,6 +119,7 @@ type=0 wordorderLittle=true optimizer=true fetch_max_blocks=false +serial_strict_timing=false [MachineSetup] capacity=7 diff --git a/src/includes/Machines/TRINITAS/T7_legacy.aset b/src/includes/Machines/TRINITAS/T7_legacy.aset index fd0c83e97..d6ba19bb0 100644 --- a/src/includes/Machines/TRINITAS/T7_legacy.aset +++ b/src/includes/Machines/TRINITAS/T7_legacy.aset @@ -119,6 +119,7 @@ type=0 wordorderLittle=true optimizer=true fetch_max_blocks=false +serial_strict_timing=false [MachineSetup] capacity=7