Skip to content

Commit ae266cf

Browse files
authored
DictTransactionManager -> ModbusTransactionManager (#2042)
1 parent c0a782d commit ae266cf

File tree

3 files changed

+52
-99
lines changed

3 files changed

+52
-99
lines changed

pymodbus/client/base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from pymodbus.framer import FRAMER_NAME_TO_CLASS, Framer, ModbusFramer
1313
from pymodbus.logging import Log
1414
from pymodbus.pdu import ModbusRequest, ModbusResponse
15-
from pymodbus.transaction import DictTransactionManager
15+
from pymodbus.transaction import ModbusTransactionManager
1616
from pymodbus.transport import CommParams, ModbusProtocol
1717
from pymodbus.utilities import ModbusTransactionState
1818

@@ -92,7 +92,7 @@ def __init__(
9292
self.framer = FRAMER_NAME_TO_CLASS.get(
9393
framer, cast(Type[ModbusFramer], framer)
9494
)(ClientDecoder(), self)
95-
self.transaction = DictTransactionManager(
95+
self.transaction = ModbusTransactionManager(
9696
self, retries=retries, retry_on_empty=retry_on_empty, **kwargs
9797
)
9898
self.use_udp = False
@@ -341,7 +341,7 @@ def __init__(
341341
self.framer = FRAMER_NAME_TO_CLASS.get(
342342
framer, cast(Type[ModbusFramer], framer)
343343
)(ClientDecoder(), self)
344-
self.transaction = DictTransactionManager(
344+
self.transaction = ModbusTransactionManager(
345345
self, retries=retries, retry_on_empty=retry_on_empty, **kwargs
346346
)
347347
self.reconnect_delay_current = self.params.reconnect_delay or 0

pymodbus/transaction.py

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
__all__ = [
44
"DictTransactionManager",
5+
"ModbusTransactionManager",
56
"ModbusSocketFramer",
67
"ModbusTlsFramer",
78
"ModbusRtuFramer",
@@ -19,7 +20,6 @@
1920
from pymodbus.exceptions import (
2021
InvalidMessageReceivedException,
2122
ModbusIOException,
22-
NotImplementedException,
2323
)
2424
from pymodbus.framer.ascii_framer import ModbusAsciiFramer
2525
from pymodbus.framer.binary_framer import ModbusBinaryFramer
@@ -47,6 +47,8 @@ class ModbusTransactionManager:
4747
while (count < 3)
4848
4949
This module helps to abstract this away from the framer and protocol.
50+
51+
Results are keyed based on the supplied transaction id.
5052
"""
5153

5254
def __init__(self, client, **kwargs):
@@ -62,11 +64,19 @@ def __init__(self, client, **kwargs):
6264
self.retry_on_empty = kwargs.get("retry_on_empty", False)
6365
self.retry_on_invalid = kwargs.get("retry_on_invalid", False)
6466
self.retries = kwargs.get("retries", 3)
67+
self.transactions = {}
6568
self._transaction_lock = RLock()
6669
self._no_response_devices = []
6770
if client:
6871
self._set_adu_size()
6972

73+
def __iter__(self):
74+
"""Iterate over the current managed transactions.
75+
76+
:returns: An iterator of the managed transactions
77+
"""
78+
return iter(self.transactions.keys())
79+
7080
def _set_adu_size(self):
7181
"""Set adu size."""
7282
# base ADU size of modbus frame in bytes
@@ -422,27 +432,33 @@ def addTransaction(self, request, tid=None):
422432
423433
:param request: The request to hold on to
424434
:param tid: The overloaded transaction id to use
425-
:raises NotImplementedException:
426435
"""
427-
raise NotImplementedException("addTransaction")
436+
tid = tid if tid is not None else request.transaction_id
437+
Log.debug("Adding transaction {}", tid)
438+
self.transactions[tid] = request
428439

429440
def getTransaction(self, tid):
430441
"""Return a transaction matching the referenced tid.
431442
432443
If the transaction does not exist, None is returned
433444
434445
:param tid: The transaction to retrieve
435-
:raises NotImplementedException:
446+
436447
"""
437-
raise NotImplementedException("getTransaction")
448+
Log.debug("Getting transaction {}", tid)
449+
if not tid:
450+
if self.transactions:
451+
return self.transactions.popitem()[1]
452+
return None
453+
return self.transactions.pop(tid, None)
438454

439455
def delTransaction(self, tid):
440456
"""Remove a transaction matching the referenced tid.
441457
442458
:param tid: The transaction to remove
443-
:raises NotImplementedException:
444459
"""
445-
raise NotImplementedException("delTransaction")
460+
Log.debug("deleting transaction {}", tid)
461+
self.transactions.pop(tid, None)
446462

447463
def getNextTID(self):
448464
"""Retrieve the next unique transaction identifier.
@@ -458,64 +474,7 @@ def getNextTID(self):
458474
def reset(self):
459475
"""Reset the transaction identifier."""
460476
self.tid = 0
461-
self.transactions = type( # pylint: disable=attribute-defined-outside-init
462-
self.transactions
463-
)()
464-
465-
466-
class DictTransactionManager(ModbusTransactionManager):
467-
"""Implements a transaction for a manager.
468-
469-
Where the results are keyed based on the supplied transaction id.
470-
"""
471-
472-
def __init__(self, client, **kwargs):
473-
"""Initialize an instance of the ModbusTransactionManager.
474-
475-
:param client: The client socket wrapper
476-
"""
477477
self.transactions = {}
478-
super().__init__(client, **kwargs)
479-
480-
def __iter__(self):
481-
"""Iterate over the current managed transactions.
482-
483-
:returns: An iterator of the managed transactions
484-
"""
485-
return iter(self.transactions.keys())
486-
487-
def addTransaction(self, request, tid=None):
488-
"""Add a transaction to the handler.
489-
490-
This holds the requests in case it needs to be resent.
491-
After being sent, the request is removed.
492-
493-
:param request: The request to hold on to
494-
:param tid: The overloaded transaction id to use
495-
"""
496-
tid = tid if tid is not None else request.transaction_id
497-
Log.debug("Adding transaction {}", tid)
498-
self.transactions[tid] = request
499-
500-
def getTransaction(self, tid):
501-
"""Return a transaction matching the referenced tid.
502-
503-
If the transaction does not exist, None is returned
504478

505-
:param tid: The transaction to retrieve
506-
507-
"""
508-
Log.debug("Getting transaction {}", tid)
509-
if not tid:
510-
if self.transactions:
511-
return self.transactions.popitem()[1]
512-
return None
513-
return self.transactions.pop(tid, None)
514-
515-
def delTransaction(self, tid):
516-
"""Remove a transaction matching the referenced tid.
517-
518-
:param tid: The transaction to remove
519-
"""
520-
Log.debug("deleting transaction {}", tid)
521-
self.transactions.pop(tid, None)
479+
class DictTransactionManager(ModbusTransactionManager):
480+
"""Old alias for ModbusTransactionManager."""

test/test_transaction.py

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from pymodbus.factory import ServerDecoder
1313
from pymodbus.pdu import ModbusRequest
1414
from pymodbus.transaction import (
15-
DictTransactionManager,
1615
ModbusAsciiFramer,
1716
ModbusBinaryFramer,
1817
ModbusRtuFramer,
@@ -50,24 +49,23 @@ def setup_method(self):
5049
self._rtu = ModbusRtuFramer(decoder=self.decoder, client=None)
5150
self._ascii = ModbusAsciiFramer(decoder=self.decoder, client=None)
5251
self._binary = ModbusBinaryFramer(decoder=self.decoder, client=None)
53-
self._manager = DictTransactionManager(self.client)
54-
self._tm = ModbusTransactionManager(self.client)
52+
self._manager = ModbusTransactionManager(self.client)
5553

5654
# ----------------------------------------------------------------------- #
57-
# Base transaction manager
55+
# Modbus transaction manager
5856
# ----------------------------------------------------------------------- #
5957

6058
def test_calculate_expected_response_length(self):
6159
"""Test calculate expected response length."""
62-
self._tm.client = mock.MagicMock()
63-
self._tm.client.framer = mock.MagicMock()
64-
self._tm._set_adu_size() # pylint: disable=protected-access
65-
assert not self._tm._calculate_response_length( # pylint: disable=protected-access
60+
self._manager.client = mock.MagicMock()
61+
self._manager.client.framer = mock.MagicMock()
62+
self._manager._set_adu_size() # pylint: disable=protected-access
63+
assert not self._manager._calculate_response_length( # pylint: disable=protected-access
6664
0
6765
)
68-
self._tm.base_adu_size = 10
66+
self._manager.base_adu_size = 10
6967
assert (
70-
self._tm._calculate_response_length(5) # pylint: disable=protected-access
68+
self._manager._calculate_response_length(5) # pylint: disable=protected-access
7169
== 15
7270
)
7371

@@ -81,23 +79,23 @@ def test_calculate_exception_length(self):
8179
("tls", 2),
8280
("dummy", None),
8381
):
84-
self._tm.client = mock.MagicMock()
82+
self._manager.client = mock.MagicMock()
8583
if framer == "ascii":
86-
self._tm.client.framer = self._ascii
84+
self._manager.client.framer = self._ascii
8785
elif framer == "binary":
88-
self._tm.client.framer = self._binary
86+
self._manager.client.framer = self._binary
8987
elif framer == "rtu":
90-
self._tm.client.framer = self._rtu
88+
self._manager.client.framer = self._rtu
9189
elif framer == "tcp":
92-
self._tm.client.framer = self._tcp
90+
self._manager.client.framer = self._tcp
9391
elif framer == "tls":
94-
self._tm.client.framer = self._tls
92+
self._manager.client.framer = self._tls
9593
else:
96-
self._tm.client.framer = mock.MagicMock()
94+
self._manager.client.framer = mock.MagicMock()
9795

98-
self._tm._set_adu_size() # pylint: disable=protected-access
96+
self._manager._set_adu_size() # pylint: disable=protected-access
9997
assert (
100-
self._tm._calculate_exception_length() # pylint: disable=protected-access
98+
self._manager._calculate_exception_length() # pylint: disable=protected-access
10199
== exception_length
102100
)
103101

@@ -140,7 +138,7 @@ def test_execute(self, mock_time):
140138
trans._recv = mock.MagicMock( # pylint: disable=protected-access
141139
return_value=b"abcdef"
142140
)
143-
trans.transactions = []
141+
trans.transactions = {}
144142
trans.getTransaction = mock.MagicMock()
145143
trans.getTransaction.return_value = None
146144
response = trans.execute(request)
@@ -198,19 +196,15 @@ def test_execute(self, mock_time):
198196
recv.assert_called_once_with(8, False)
199197
client.comm_params.handle_local_echo = False
200198

201-
# ----------------------------------------------------------------------- #
202-
# Dictionary based transaction manager
203-
# ----------------------------------------------------------------------- #
204-
205-
def test_dict_transaction_manager_tid(self):
206-
"""Test the dict transaction manager TID."""
199+
def test_transaction_manager_tid(self):
200+
"""Test the transaction manager TID."""
207201
for tid in range(1, self._manager.getNextTID() + 10):
208202
assert tid + 1 == self._manager.getNextTID()
209203
self._manager.reset()
210204
assert self._manager.getNextTID() == 1
211205

212-
def test_get_dict_fifo_transaction_manager_transaction(self):
213-
"""Test the dict transaction manager."""
206+
def test_get_transaction_manager_transaction(self):
207+
"""Test the getting a transaction from the transaction manager."""
214208

215209
class Request: # pylint: disable=too-few-public-methods
216210
"""Request."""
@@ -225,8 +219,8 @@ class Request: # pylint: disable=too-few-public-methods
225219
result = self._manager.getTransaction(handle.transaction_id)
226220
assert handle.message == result.message
227221

228-
def test_delete_dict_fifo_transaction_manager_transaction(self):
229-
"""Test the dict transaction manager."""
222+
def test_delete_transaction_manager_transaction(self):
223+
"""Test deleting a transaction from the dict transaction manager."""
230224

231225
class Request: # pylint: disable=too-few-public-methods
232226
"""Request."""

0 commit comments

Comments
 (0)