diff --git a/pymodbus/pdu/__init__.py b/pymodbus/pdu/__init__.py index 952bd9ade..6d023a199 100644 --- a/pymodbus/pdu/__init__.py +++ b/pymodbus/pdu/__init__.py @@ -2,11 +2,11 @@ __all__ = [ "DecodePDU", "ExceptionResponse", - "ExceptionResponse", "FileRecord", "ModbusPDU", ] -from pymodbus.pdu.decoders import DecodePDU -from pymodbus.pdu.file_message import FileRecord -from pymodbus.pdu.pdu import ExceptionResponse, ModbusPDU +from .decoders import DecodePDU +from .exceptionresponse import ExceptionResponse +from .file_message import FileRecord +from .pdu import ModbusPDU diff --git a/pymodbus/pdu/bit_message.py b/pymodbus/pdu/bit_message.py index a82f3d1e3..f2d22b50d 100644 --- a/pymodbus/pdu/bit_message.py +++ b/pymodbus/pdu/bit_message.py @@ -6,7 +6,8 @@ from pymodbus.constants import ModbusStatus from pymodbus.datastore import ModbusDeviceContext -from .pdu import ExceptionResponse, ModbusPDU, pack_bitstring, unpack_bitstring +from .exceptionresponse import ExceptionResponse +from .pdu import ModbusPDU, pack_bitstring, unpack_bitstring class ReadCoilsRequest(ModbusPDU): diff --git a/pymodbus/pdu/device.py b/pymodbus/pdu/device.py index 9303dfcc7..9fa5b72e0 100644 --- a/pymodbus/pdu/device.py +++ b/pymodbus/pdu/device.py @@ -19,9 +19,10 @@ from collections import OrderedDict from pymodbus.constants import DeviceInformation -from pymodbus.pdu.events import ModbusEvent from pymodbus.utilities import dict_property +from .events import ModbusEvent + # ---------------------------------------------------------------------------# # Modbus Plus Statistics diff --git a/pymodbus/pdu/diag_message.py b/pymodbus/pdu/diag_message.py index 3dd603f8f..7a69a9e5b 100644 --- a/pymodbus/pdu/diag_message.py +++ b/pymodbus/pdu/diag_message.py @@ -6,8 +6,8 @@ from pymodbus.constants import ModbusPlusOperation from pymodbus.datastore import ModbusDeviceContext -from pymodbus.pdu.device import ModbusControlBlock +from .device import ModbusControlBlock from .pdu import ModbusPDU, pack_bitstring diff --git a/pymodbus/pdu/exceptionresponse.py b/pymodbus/pdu/exceptionresponse.py new file mode 100644 index 000000000..53cbcf44e --- /dev/null +++ b/pymodbus/pdu/exceptionresponse.py @@ -0,0 +1,51 @@ +"""Contains exceptionResponse class for modbus.""" +from __future__ import annotations + +import struct + +from .pdu import ModbusPDU + + +class ExceptionResponse(ModbusPDU): + """Base class for a modbus exception PDU.""" + + rtu_frame_size = 5 + + ILLEGAL_FUNCTION = 0x01 + ILLEGAL_ADDRESS = 0x02 + ILLEGAL_VALUE = 0x03 + DEVICE_FAILURE = 0x04 + ACKNOWLEDGE = 0x05 + DEVICE_BUSY = 0x06 + NEGATIVE_ACKNOWLEDGE = 0x07 + MEMORY_PARITY_ERROR = 0x08 + GATEWAY_PATH_UNAVIABLE = 0x0A + GATEWAY_NO_RESPONSE = 0x0B + + def __init__( + self, + function_code: int, + exception_code: int = 0, + device_id: int = 1, + transaction: int = 0) -> None: + """Initialize the modbus exception response.""" + super().__init__(transaction_id=transaction, dev_id=device_id) + self.function_code = function_code | 0x80 + self.exception_code = exception_code + + def __str__(self) -> str: + """Build a representation of an exception response.""" + return ( + f"{self.__class__.__name__}(" + f"dev_id={self.dev_id}, " + f"function_code={self.function_code}, " + f"exception_code={self.exception_code})" + ) + + def encode(self) -> bytes: + """Encode a modbus exception response.""" + return struct.pack(">B", self.exception_code) + + def decode(self, data: bytes) -> None: + """Decode a modbus exception response.""" + self.exception_code = int(data[0]) diff --git a/pymodbus/pdu/file_message.py b/pymodbus/pdu/file_message.py index 27ea7bee4..59ee80b9d 100644 --- a/pymodbus/pdu/file_message.py +++ b/pymodbus/pdu/file_message.py @@ -6,7 +6,8 @@ from pymodbus.datastore import ModbusDeviceContext from pymodbus.exceptions import ModbusException -from pymodbus.pdu.pdu import ModbusPDU + +from .pdu import ModbusPDU @dataclass diff --git a/pymodbus/pdu/mei_message.py b/pymodbus/pdu/mei_message.py index 455aaba26..1829fb0b7 100644 --- a/pymodbus/pdu/mei_message.py +++ b/pymodbus/pdu/mei_message.py @@ -5,8 +5,10 @@ from pymodbus.constants import DeviceInformation, MoreData from pymodbus.datastore import ModbusDeviceContext -from pymodbus.pdu.device import DeviceInformationFactory, ModbusControlBlock -from pymodbus.pdu.pdu import ExceptionResponse, ModbusPDU + +from .device import DeviceInformationFactory, ModbusControlBlock +from .exceptionresponse import ExceptionResponse +from .pdu import ModbusPDU _MCB = ModbusControlBlock() diff --git a/pymodbus/pdu/other_message.py b/pymodbus/pdu/other_message.py index cd58cb1f4..0bf13cdb9 100644 --- a/pymodbus/pdu/other_message.py +++ b/pymodbus/pdu/other_message.py @@ -5,8 +5,9 @@ from pymodbus.constants import ModbusStatus from pymodbus.datastore import ModbusDeviceContext -from pymodbus.pdu.device import DeviceInformationFactory, ModbusControlBlock -from pymodbus.pdu.pdu import ModbusPDU + +from .device import DeviceInformationFactory, ModbusControlBlock +from .pdu import ModbusPDU _MCB = ModbusControlBlock() diff --git a/pymodbus/pdu/pdu.py b/pymodbus/pdu/pdu.py index 30ea95a61..e857cba5d 100644 --- a/pymodbus/pdu/pdu.py +++ b/pymodbus/pdu/pdu.py @@ -87,7 +87,9 @@ def decode(self, data: bytes) -> None: async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU: """Run request against a datastore.""" _ = context - return ExceptionResponse(0, 0) + raise NotImplementedException( + f"update datastore called wrongly {self.__class__.__name__}" + ) @classmethod def calculateRtuFrameSize(cls, data: bytes) -> int: diff --git a/pymodbus/pdu/register_message.py b/pymodbus/pdu/register_message.py index 1bb3da059..f5f7bc02d 100644 --- a/pymodbus/pdu/register_message.py +++ b/pymodbus/pdu/register_message.py @@ -8,7 +8,9 @@ from pymodbus.datastore import ModbusDeviceContext from pymodbus.exceptions import ModbusIOException -from pymodbus.pdu.pdu import ExceptionResponse, ModbusPDU + +from .exceptionresponse import ExceptionResponse +from .pdu import ModbusPDU class ReadHoldingRegistersRequest(ModbusPDU): diff --git a/test/pdu/test_pdu.py b/test/pdu/test_pdu.py index e4108528e..036c32a05 100644 --- a/test/pdu/test_pdu.py +++ b/test/pdu/test_pdu.py @@ -250,7 +250,8 @@ async def test_pdu_default_datastore(self, mock_context): """Test that all PDU types can be created.""" pdu = ModbusPDU() context = mock_context() - assert await pdu.update_datastore(context) + with pytest.raises(NotImplementedException): + assert await pdu.update_datastore(context) @pytest.mark.parametrize( ("bytestream", "bitlist"),