Skip to content

Commit 47733d2

Browse files
committed
Merge branch 'development' into RTOP-100-OPC-UA
2 parents 9ab9a34 + 414bb91 commit 47733d2

7 files changed

Lines changed: 684 additions & 44 deletions

File tree

core/src/drivers/plugins/python/modbus_master/modbus_master_connection.py

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,47 @@
11
"""Modbus Master plugin connection management utilities."""
22

33
import time
4-
from typing import Optional
4+
from typing import Literal, Optional, Union
55

6-
from pymodbus.client import ModbusTcpClient
6+
from pymodbus.client import ModbusTcpClient, ModbusSerialClient
7+
8+
TransportType = Literal["tcp", "rtu"]
9+
ParityType = Literal["N", "E", "O"]
710

811

912
class ModbusConnectionManager: # pylint: disable=too-many-instance-attributes
10-
"""Manages Modbus TCP connections with retry logic."""
13+
"""Manages Modbus TCP and RTU connections with retry logic."""
14+
15+
def __init__(
16+
self,
17+
transport: TransportType = "tcp",
18+
# TCP parameters
19+
host: str = "127.0.0.1",
20+
port: int = 502,
21+
# RTU parameters
22+
serial_port: str = "",
23+
baud_rate: int = 9600,
24+
parity: ParityType = "N",
25+
stop_bits: int = 1,
26+
data_bits: int = 8,
27+
# Common parameters
28+
timeout_ms: int = 1000,
29+
slave_id: int = 1,
30+
):
31+
self.transport = transport
32+
self.timeout = timeout_ms / 1000.0 # Convert to seconds
33+
self.slave_id = slave_id # Unit/Slave ID for Modbus operations
1134

12-
def __init__(self, host: str, port: int, timeout_ms: int):
35+
# TCP configuration
1336
self.host = host
1437
self.port = port
15-
self.timeout = timeout_ms / 1000.0 # Convert to seconds
38+
39+
# RTU configuration
40+
self.serial_port = serial_port
41+
self.baud_rate = baud_rate
42+
self.parity = parity
43+
self.stop_bits = stop_bits
44+
self.data_bits = data_bits
1645

1746
# Retry configuration
1847
self.retry_delay_base = 2.0 # initial delay between attempts (seconds)
@@ -21,9 +50,34 @@ def __init__(self, host: str, port: int, timeout_ms: int):
2150

2251
# Connection state - is_connected is the authoritative flag for connection health
2352
# It is set to False when any error occurs, forcing reconnection on next cycle
24-
self.client: Optional[ModbusTcpClient] = None
53+
self.client: Optional[Union[ModbusTcpClient, ModbusSerialClient]] = None
2554
self.is_connected = False
2655

56+
def _create_client(self) -> Union[ModbusTcpClient, ModbusSerialClient]:
57+
"""Create the appropriate Modbus client based on transport type."""
58+
if self.transport == "tcp":
59+
return ModbusTcpClient(
60+
host=self.host,
61+
port=self.port,
62+
timeout=self.timeout
63+
)
64+
else: # RTU
65+
return ModbusSerialClient(
66+
port=self.serial_port,
67+
baudrate=self.baud_rate,
68+
parity=self.parity,
69+
stopbits=self.stop_bits,
70+
bytesize=self.data_bits,
71+
timeout=self.timeout
72+
)
73+
74+
def get_connection_info(self) -> str:
75+
"""Return human-readable connection information."""
76+
if self.transport == "tcp":
77+
return f"TCP {self.host}:{self.port}"
78+
else:
79+
return f"RTU {self.serial_port}@{self.baud_rate}"
80+
2781
def connect_with_retry(self, stop_event=None) -> bool:
2882
"""
2983
Attempts to connect to Modbus device with infinite retry.
@@ -35,6 +89,7 @@ def connect_with_retry(self, stop_event=None) -> bool:
3589
True if connected successfully, False if interrupted
3690
"""
3791
retry_count = 0
92+
conn_info = self.get_connection_info()
3893

3994
while stop_event is None or not stop_event.is_set():
4095
try:
@@ -45,30 +100,28 @@ def connect_with_retry(self, stop_event=None) -> bool:
45100
self.client.close()
46101
except Exception:
47102
pass
48-
self.client = ModbusTcpClient(
49-
host=self.host, port=self.port, timeout=self.timeout
50-
)
103+
self.client = self._create_client()
51104

52105
# Attempt to connect
53106
if self.client.connect():
54107
print(
55-
f"(PASS) Connected to {self.host}:{self.port} (attempt {retry_count + 1})"
108+
f"(PASS) Connected to {conn_info} (attempt {retry_count + 1})"
56109
)
57110
self.is_connected = True
58111
self.retry_delay_current = self.retry_delay_base # Reset delay
59112
return True
60113

61114
except Exception as e:
62-
print(f"(FAIL) Connection attempt {retry_count + 1} failed: {e}")
115+
print(f"(FAIL) Connection attempt {retry_count + 1} to {conn_info} failed: {e}")
63116

64117
# Increment counter and calculate delay
65118
retry_count += 1
66119

67120
# Attempt logging
68121
if retry_count == 1:
69-
print(f"Failed to connect to {self.host}:{self.port}, starting retry attempts...")
122+
print(f"Failed to connect to {conn_info}, starting retry attempts...")
70123
elif retry_count % 10 == 0: # Log every 10 attempts
71-
print(f"Connection attempt {retry_count} failed, continuing retries...")
124+
print(f"Connection attempt {retry_count} to {conn_info} failed, continuing retries...")
72125

73126
# Wait with increasing delay (limited exponential backoff)
74127
delay = min(self.retry_delay_current, self.retry_delay_max)
@@ -127,21 +180,23 @@ def mark_disconnected(self):
127180
ModbusIOException, etc.) to ensure the connection is properly re-established.
128181
"""
129182
self.is_connected = False
183+
conn_info = self.get_connection_info()
130184
print(
131-
f"Connection to {self.host}:{self.port} marked as disconnected, "
185+
f"Connection to {conn_info} marked as disconnected, "
132186
"will reconnect on next cycle"
133187
)
134188

135189
def disconnect(self):
136190
"""Close the connection and clean up resources."""
191+
conn_info = self.get_connection_info()
137192
try:
138193
if self.client:
139194
self.client.close()
140195
self.client = None
141196
self.is_connected = False
142-
print(f"Disconnected from {self.host}:{self.port}")
197+
print(f"Disconnected from {conn_info}")
143198
except Exception as e:
144-
print(f"(FAIL) Error disconnecting from {self.host}:{self.port}: {e}")
199+
print(f"(FAIL) Error disconnecting from {conn_info}: {e}")
145200

146201
def is_healthy(self) -> bool:
147202
"""

0 commit comments

Comments
 (0)