diff --git a/README.rst b/README.rst index d20346a2e..ddd773472 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,7 @@ PyModbus - A Python Modbus Stack :target: https://gurubase.io/g/pymodbus :alt: PyModbus Guru -Pymodbus is a full Modbus protocol implementation offering client/server with synchronous/asynchronous API a well as simulators. +Pymodbus is a full Modbus protocol implementation offering client/server with synchronous/asynchronous API and simulators. Our releases is defined as X.Y.Z, and we have strict rules what to release when: diff --git a/doc/source/_static/examples.tgz b/doc/source/_static/examples.tgz index b3f010b50..4f8760337 100644 Binary files a/doc/source/_static/examples.tgz and b/doc/source/_static/examples.tgz differ diff --git a/doc/source/_static/examples.zip b/doc/source/_static/examples.zip index 88b7f742e..9f48d8be8 100644 Binary files a/doc/source/_static/examples.zip and b/doc/source/_static/examples.zip differ diff --git a/doc/source/library/pymodbus.rst b/doc/source/library/pymodbus.rst index cdda34602..610a440d1 100644 --- a/doc/source/library/pymodbus.rst +++ b/doc/source/library/pymodbus.rst @@ -21,11 +21,6 @@ Extra functions :undoc-members: :show-inheritance: -.. automodule:: pymodbus.factory - :members: - :undoc-members: - :show-inheritance: - .. automodule:: pymodbus.payload :members: :undoc-members: @@ -45,6 +40,11 @@ Extra functions PDU classes =========== +.. automodule:: pymodbus.pdu.decoders + :members: + :undoc-members: + :show-inheritance: + .. automodule:: pymodbus.pdu.bit_read_message :members: :undoc-members: diff --git a/doc/source/repl.rst b/doc/source/repl.rst index a598c7904..28ad16284 100644 --- a/doc/source/repl.rst +++ b/doc/source/repl.rst @@ -3,7 +3,8 @@ Pymodbus REPL (Read Evaluate Print Loop) .. raw:: html -
Warning: The Pymodbus REPL documentation is not updated.
+Warning: The Pymodbus REPL documentation is not updated, + because it lives in a different repo.
Installation ------------ diff --git a/doc/source/roadmap.rst b/doc/source/roadmap.rst index 610a08d9c..c29d872e2 100644 --- a/doc/source/roadmap.rst +++ b/doc/source/roadmap.rst @@ -16,13 +16,18 @@ It is the community that decides how pymodbus evolves NOT the maintainers ! The following bullet points are what the maintainers focus on: - 3.7.4, bug fix release, hopefully with: - - optimized framer, limited support for multidrop on the server side - - more typing in the core - - 100% test coverage fixed for all new parts (currently transport and framer) + - Available on dev: + - optimized framer, limited support for multidrop on the server side + - more typing in the core + - 100% test coverage fixed for all new parts (currently transport and framer) + - Updated PDU, moving client/server decoder into pdu + - better broadcast handling - better broadcast handling -- 3.7.5, bug fix release, hopefully with: - - Updated PDU, moving client/server decoder into pdu. - Simplify PDU classes + - better retry handling (only disconnect when really needed) +- 3.7.5, bug fix release, hopefully with: + - 100% test coverage for pdu + - better broadcast handling - 3.7.6, bug fix release, with: - Foundation for new transaction - 3.8.0, with: diff --git a/examples/message_parser.py b/examples/message_parser.py index d96c57b64..fb76217e5 100755 --- a/examples/message_parser.py +++ b/examples/message_parser.py @@ -12,12 +12,12 @@ import textwrap from pymodbus import pymodbus_apply_logging_config -from pymodbus.factory import ClientDecoder, ServerDecoder from pymodbus.framer import ( FramerAscii, FramerRTU, FramerSocket, ) +from pymodbus.pdu import ClientDecoder, ServerDecoder _logger = logging.getLogger(__file__) diff --git a/pymodbus/client/base.py b/pymodbus/client/base.py index 55badc94a..f70703cb2 100644 --- a/pymodbus/client/base.py +++ b/pymodbus/client/base.py @@ -9,10 +9,9 @@ from pymodbus.client.mixin import ModbusClientMixin from pymodbus.client.modbusclientprotocol import ModbusClientProtocol from pymodbus.exceptions import ConnectionException, ModbusIOException -from pymodbus.factory import ClientDecoder from pymodbus.framer import FRAMER_NAME_TO_CLASS, FramerBase, FramerType from pymodbus.logging import Log -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu import ClientDecoder, ModbusPDU from pymodbus.transaction import SyncModbusTransactionManager from pymodbus.transport import CommParams from pymodbus.utilities import ModbusTransactionState diff --git a/pymodbus/client/modbusclientprotocol.py b/pymodbus/client/modbusclientprotocol.py index e582366df..c9fae6960 100644 --- a/pymodbus/client/modbusclientprotocol.py +++ b/pymodbus/client/modbusclientprotocol.py @@ -3,13 +3,13 @@ from collections.abc import Callable -from pymodbus.factory import ClientDecoder from pymodbus.framer import ( FRAMER_NAME_TO_CLASS, FramerBase, FramerType, ) from pymodbus.logging import Log +from pymodbus.pdu import ClientDecoder from pymodbus.transaction import ModbusTransactionManager from pymodbus.transport import CommParams, ModbusProtocol diff --git a/pymodbus/device.py b/pymodbus/device.py index 61d97e8db..b35533947 100644 --- a/pymodbus/device.py +++ b/pymodbus/device.py @@ -233,7 +233,7 @@ def __str__(self): class DeviceInformationFactory: # pylint: disable=too-few-public-methods - """This is a helper factory. + """This is a helper. That really just hides some of the complexity of processing the device information diff --git a/pymodbus/framer/base.py b/pymodbus/framer/base.py index 3fe6ad531..5689bddac 100644 --- a/pymodbus/framer/base.py +++ b/pymodbus/framer/base.py @@ -9,9 +9,8 @@ from enum import Enum from pymodbus.exceptions import ModbusIOException -from pymodbus.factory import ClientDecoder, ServerDecoder from pymodbus.logging import Log -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu import ClientDecoder, ModbusPDU, ServerDecoder class FramerType(str, Enum): diff --git a/pymodbus/pdu/__init__.py b/pymodbus/pdu/__init__.py index e7aab71e6..702b25708 100644 --- a/pymodbus/pdu/__init__.py +++ b/pymodbus/pdu/__init__.py @@ -1,10 +1,13 @@ """Framer.""" __all__ = [ + "ClientDecoder", "ExceptionResponse", "ModbusExceptions", "ModbusPDU", + "ServerDecoder" ] +from pymodbus.pdu.decoders import ClientDecoder, ServerDecoder from pymodbus.pdu.pdu import ( ExceptionResponse, ModbusExceptions, diff --git a/pymodbus/pdu/bit_read_message.py b/pymodbus/pdu/bit_read_message.py index bd27bc58e..6906da3f9 100644 --- a/pymodbus/pdu/bit_read_message.py +++ b/pymodbus/pdu/bit_read_message.py @@ -3,8 +3,8 @@ # pylint: disable=missing-type-doc import struct -from pymodbus.pdu import ModbusExceptions as merror -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu.pdu import ModbusExceptions as merror +from pymodbus.pdu.pdu import ModbusPDU from pymodbus.utilities import pack_bitstring, unpack_bitstring diff --git a/pymodbus/pdu/bit_write_message.py b/pymodbus/pdu/bit_write_message.py index 8aed89360..0b7496306 100644 --- a/pymodbus/pdu/bit_write_message.py +++ b/pymodbus/pdu/bit_write_message.py @@ -8,8 +8,8 @@ import struct from pymodbus.constants import ModbusStatus -from pymodbus.pdu import ModbusExceptions as merror -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu.pdu import ModbusExceptions as merror +from pymodbus.pdu.pdu import ModbusPDU from pymodbus.utilities import pack_bitstring, unpack_bitstring diff --git a/pymodbus/factory.py b/pymodbus/pdu/decoders.py similarity index 92% rename from pymodbus/factory.py rename to pymodbus/pdu/decoders.py index bd9746f2d..f6f58e4f7 100644 --- a/pymodbus/factory.py +++ b/pymodbus/pdu/decoders.py @@ -12,17 +12,17 @@ # pylint: disable=missing-type-doc from collections.abc import Callable +import pymodbus.pdu.bit_read_message as bit_r_msg +import pymodbus.pdu.bit_write_message as bit_w_msg +import pymodbus.pdu.diag_message as diag_msg +import pymodbus.pdu.file_message as file_msg +import pymodbus.pdu.mei_message as mei_msg +import pymodbus.pdu.other_message as o_msg +import pymodbus.pdu.pdu as base +import pymodbus.pdu.register_read_message as reg_r_msg +import pymodbus.pdu.register_write_message as reg_w_msg from pymodbus.exceptions import MessageRegisterException, ModbusException from pymodbus.logging import Log -from pymodbus.pdu import bit_read_message as bit_r_msg -from pymodbus.pdu import bit_write_message as bit_w_msg -from pymodbus.pdu import diag_message as diag_msg -from pymodbus.pdu import file_message as file_msg -from pymodbus.pdu import mei_message as mei_msg -from pymodbus.pdu import other_message as o_msg -from pymodbus.pdu import pdu -from pymodbus.pdu import register_read_message as reg_r_msg -from pymodbus.pdu import register_write_message as reg_w_msg # --------------------------------------------------------------------------- # @@ -107,7 +107,7 @@ def lookupPduClass(self, function_code): :param function_code: The function code specified in a frame. :returns: The class of the PDU that has a matching `function_code`. """ - return self.lookup.get(function_code, pdu.ExceptionResponse) + return self.lookup.get(function_code, base.ExceptionResponse) def _helper(self, data: str): """Generate the correct request object from a valid request packet. @@ -120,9 +120,9 @@ def _helper(self, data: str): function_code = int(data[0]) if not (request := self.lookup.get(function_code, lambda: None)()): Log.debug("Factory Request[{}]", function_code) - request = pdu.ExceptionResponse( + request = base.ExceptionResponse( function_code, - exception_code=pdu.ModbusExceptions.IllegalFunction, + exception_code=base.ModbusExceptions.IllegalFunction, slave=0, transaction=0, skip_encode=False) @@ -149,7 +149,7 @@ def register(self, function): :param function: Custom function class to register :raises MessageRegisterException: """ - if not issubclass(function, pdu.ModbusPDU): + if not issubclass(function, base.ModbusPDU): raise MessageRegisterException( f'"{function.__class__.__name__}" is Not a valid Modbus Message' ". Class needs to be derived from " @@ -229,7 +229,7 @@ def lookupPduClass(self, function_code): :param function_code: The function code specified in a frame. :returns: The class of the PDU that has a matching `function_code`. """ - return self.lookup.get(function_code, pdu.ExceptionResponse) + return self.lookup.get(function_code, base.ExceptionResponse) def decode(self, message): """Decode a response packet. @@ -265,7 +265,7 @@ def _helper(self, data: str): response = self.lookup.get(function_code, lambda: None)() if function_code > 0x80: code = function_code & 0x7F # strip error portion - response = pdu.ExceptionResponse(code, pdu.ModbusExceptions.IllegalFunction) + response = base.ExceptionResponse(code, base.ModbusExceptions.IllegalFunction) if not response: raise ModbusException(f"Unknown response {function_code}") response.decode(data[1:]) @@ -279,7 +279,7 @@ def _helper(self, data: str): def register(self, function): """Register a function and sub function class with the decoder.""" - if function and not issubclass(function, pdu.ModbusPDU): + if function and not issubclass(function, base.ModbusPDU): raise MessageRegisterException( f'"{function.__class__.__name__}" is Not a valid Modbus Message' ". Class needs to be derived from " diff --git a/pymodbus/pdu/diag_message.py b/pymodbus/pdu/diag_message.py index d6d9bc95d..d37265002 100644 --- a/pymodbus/pdu/diag_message.py +++ b/pymodbus/pdu/diag_message.py @@ -11,7 +11,7 @@ from pymodbus.constants import ModbusPlusOperation, ModbusStatus from pymodbus.device import ModbusControlBlock from pymodbus.exceptions import ModbusException, NotImplementedException -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu.pdu import ModbusPDU from pymodbus.utilities import pack_bitstring diff --git a/pymodbus/pdu/file_message.py b/pymodbus/pdu/file_message.py index 1102eb503..09fb746f8 100644 --- a/pymodbus/pdu/file_message.py +++ b/pymodbus/pdu/file_message.py @@ -7,8 +7,8 @@ # pylint: disable=missing-type-doc import struct -from pymodbus.pdu import ModbusExceptions as merror -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu.pdu import ModbusExceptions as merror +from pymodbus.pdu.pdu import ModbusPDU # ---------------------------------------------------------------------------# diff --git a/pymodbus/pdu/mei_message.py b/pymodbus/pdu/mei_message.py index 67b9a0e11..6f28519d1 100644 --- a/pymodbus/pdu/mei_message.py +++ b/pymodbus/pdu/mei_message.py @@ -6,8 +6,8 @@ from pymodbus.constants import DeviceInformation, MoreData from pymodbus.device import DeviceInformationFactory, ModbusControlBlock -from pymodbus.pdu import ModbusExceptions as merror -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu.pdu import ModbusExceptions as merror +from pymodbus.pdu.pdu import ModbusPDU _MCB = ModbusControlBlock() diff --git a/pymodbus/pdu/other_message.py b/pymodbus/pdu/other_message.py index 10ab7503c..37572e193 100644 --- a/pymodbus/pdu/other_message.py +++ b/pymodbus/pdu/other_message.py @@ -9,7 +9,7 @@ from pymodbus.constants import ModbusStatus from pymodbus.device import DeviceInformationFactory, ModbusControlBlock -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu.pdu import ModbusPDU _MCB = ModbusControlBlock() diff --git a/pymodbus/pdu/register_read_message.py b/pymodbus/pdu/register_read_message.py index c25c6767c..73c14cb0b 100644 --- a/pymodbus/pdu/register_read_message.py +++ b/pymodbus/pdu/register_read_message.py @@ -5,8 +5,8 @@ import struct from pymodbus.exceptions import ModbusIOException -from pymodbus.pdu import ExceptionResponse, ModbusPDU -from pymodbus.pdu import ModbusExceptions as merror +from pymodbus.pdu.pdu import ExceptionResponse, ModbusPDU +from pymodbus.pdu.pdu import ModbusExceptions as merror class ReadRegistersRequestBase(ModbusPDU): diff --git a/pymodbus/pdu/register_write_message.py b/pymodbus/pdu/register_write_message.py index ab2b3c59e..e7c3ba0ef 100644 --- a/pymodbus/pdu/register_write_message.py +++ b/pymodbus/pdu/register_write_message.py @@ -4,8 +4,8 @@ # pylint: disable=missing-type-doc import struct -from pymodbus.pdu import ModbusExceptions as merror -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu.pdu import ModbusExceptions as merror +from pymodbus.pdu.pdu import ModbusPDU class WriteSingleRegisterRequest(ModbusPDU): diff --git a/pymodbus/server/async_io.py b/pymodbus/server/async_io.py index 57e01f3f5..d347a2eb5 100644 --- a/pymodbus/server/async_io.py +++ b/pymodbus/server/async_io.py @@ -10,10 +10,10 @@ from pymodbus.datastore import ModbusServerContext from pymodbus.device import ModbusControlBlock, ModbusDeviceIdentification from pymodbus.exceptions import NoSuchSlaveException -from pymodbus.factory import ServerDecoder from pymodbus.framer import FRAMER_NAME_TO_CLASS, FramerBase, FramerType from pymodbus.logging import Log from pymodbus.pdu import ModbusExceptions as merror +from pymodbus.pdu import ServerDecoder from pymodbus.transport import CommParams, CommType, ModbusProtocol diff --git a/pymodbus/server/simulator/http_server.py b/pymodbus/server/simulator/http_server.py index dadbe5060..fb7cda698 100644 --- a/pymodbus/server/simulator/http_server.py +++ b/pymodbus/server/simulator/http_server.py @@ -24,9 +24,8 @@ from pymodbus.datastore import ModbusServerContext, ModbusSimulatorContext from pymodbus.datastore.simulator import Label from pymodbus.device import ModbusDeviceIdentification -from pymodbus.factory import ServerDecoder from pymodbus.logging import Log -from pymodbus.pdu import ExceptionResponse +from pymodbus.pdu import ExceptionResponse, ServerDecoder from pymodbus.server.async_io import ( ModbusSerialServer, ModbusTcpServer, diff --git a/test/framers/__init__.py b/test/framer/__init__.py similarity index 100% rename from test/framers/__init__.py rename to test/framer/__init__.py diff --git a/test/framers/conftest.py b/test/framer/conftest.py similarity index 90% rename from test/framers/conftest.py rename to test/framer/conftest.py index c8e80f592..a3015c7d4 100644 --- a/test/framers/conftest.py +++ b/test/framer/conftest.py @@ -3,8 +3,8 @@ import pytest -from pymodbus.factory import ClientDecoder, ServerDecoder from pymodbus.framer import FRAMER_NAME_TO_CLASS, FramerType +from pymodbus.pdu import ClientDecoder, ServerDecoder @pytest.fixture(name="entry") diff --git a/test/framers/generator.py b/test/framer/generator.py similarity index 96% rename from test/framers/generator.py rename to test/framer/generator.py index 71518c0d5..151027931 100755 --- a/test/framers/generator.py +++ b/test/framer/generator.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 """Build framer encode responses.""" -from pymodbus.factory import ClientDecoder, ServerDecoder from pymodbus.framer import ( FramerAscii, FramerRTU, FramerSocket, FramerTLS, ) +from pymodbus.pdu import ClientDecoder, ServerDecoder from pymodbus.pdu import ModbusExceptions as merror from pymodbus.pdu.register_read_message import ( ReadHoldingRegistersRequest, diff --git a/test/framers/test_extras.py b/test/framer/test_extras.py similarity index 98% rename from test/framers/test_extras.py rename to test/framer/test_extras.py index bb5d2ac48..41be63aaf 100755 --- a/test/framers/test_extras.py +++ b/test/framer/test_extras.py @@ -1,12 +1,12 @@ """Test transaction.""" -from pymodbus.factory import ServerDecoder from pymodbus.framer import ( FramerAscii, FramerRTU, FramerSocket, FramerTLS, ) +from pymodbus.pdu import ServerDecoder TEST_MESSAGE = b"\x7b\x01\x03\x00\x00\x00\x05\x85\xC9\x7d" diff --git a/test/framers/test_framer.py b/test/framer/test_framer.py similarity index 99% rename from test/framers/test_framer.py rename to test/framer/test_framer.py index 5689d5880..84df1a1ce 100644 --- a/test/framers/test_framer.py +++ b/test/framer/test_framer.py @@ -3,7 +3,6 @@ import pytest -from pymodbus.factory import ClientDecoder from pymodbus.framer import ( FramerAscii, FramerBase, @@ -12,7 +11,7 @@ FramerTLS, FramerType, ) -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu import ClientDecoder, ModbusPDU from .generator import set_calls diff --git a/test/framers/test_multidrop.py b/test/framer/test_multidrop.py similarity index 99% rename from test/framers/test_multidrop.py rename to test/framer/test_multidrop.py index 083ed4643..a68a050da 100644 --- a/test/framers/test_multidrop.py +++ b/test/framer/test_multidrop.py @@ -4,8 +4,8 @@ import pytest from pymodbus.exceptions import ModbusIOException -from pymodbus.factory import ClientDecoder, ServerDecoder from pymodbus.framer import FramerAscii, FramerRTU +from pymodbus.pdu import ClientDecoder, ServerDecoder class TestMultidrop: diff --git a/test/sub_current/test_factory.py b/test/pdu/test_decoders.py similarity index 91% rename from test/sub_current/test_factory.py rename to test/pdu/test_decoders.py index ec4040717..d45fb0dab 100644 --- a/test/sub_current/test_factory.py +++ b/test/pdu/test_decoders.py @@ -2,8 +2,8 @@ import pytest from pymodbus.exceptions import MessageRegisterException, ModbusException -from pymodbus.factory import ClientDecoder, ServerDecoder from pymodbus.pdu import ModbusPDU +from pymodbus.pdu.decoders import ClientDecoder, ServerDecoder class TestFactory: @@ -150,11 +150,23 @@ class CustomRequest(ModbusPDU): function_code = 0xFF - class NoCustomRequest: # pylint: disable=too-few-public-methods + def encode(self): + """Encode.""" + + def decode(self, _data): + """Decode.""" + + class NoCustomRequest: """Custom request.""" function_code = 0xFF + def encode(self): + """Encode.""" + + def decode(self, _data): + """Decode.""" + self.server.register(CustomRequest) assert self.client.lookupPduClass(CustomRequest.function_code) CustomRequest.sub_function_code = 0xFF @@ -175,11 +187,23 @@ class CustomResponse(ModbusPDU): function_code = 0xFF - class NoCustomResponse: # pylint: disable=too-few-public-methods + def encode(self): + """Encode.""" + + def decode(self, _data): + """Decode.""" + + class NoCustomResponse: """Custom request.""" function_code = 0xFF + def encode(self): + """Encode.""" + + def decode(self, _data): + """Decode.""" + self.client.register(CustomResponse) assert self.client.lookupPduClass(CustomResponse.function_code) CustomResponse.sub_function_code = 0xFF diff --git a/test/sub_client/test_client_faulty_response.py b/test/sub_client/test_client_faulty_response.py index 3b80503e5..b0fe671c0 100644 --- a/test/sub_client/test_client_faulty_response.py +++ b/test/sub_client/test_client_faulty_response.py @@ -3,8 +3,8 @@ import pytest from pymodbus.exceptions import ModbusIOException -from pymodbus.factory import ClientDecoder from pymodbus.framer import FramerRTU, FramerSocket +from pymodbus.pdu import ClientDecoder class TestFaultyResponses: diff --git a/test/sub_current/test_transaction.py b/test/sub_current/test_transaction.py index 41602b845..9fa678472 100755 --- a/test/sub_current/test_transaction.py +++ b/test/sub_current/test_transaction.py @@ -4,14 +4,13 @@ from pymodbus.exceptions import ( ModbusIOException, ) -from pymodbus.factory import ServerDecoder from pymodbus.framer import ( FramerAscii, FramerRTU, FramerSocket, FramerTLS, ) -from pymodbus.pdu import ModbusPDU +from pymodbus.pdu import ModbusPDU, ServerDecoder from pymodbus.transaction import ( ModbusTransactionManager, SyncModbusTransactionManager,