Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add trace API to server. #2479

Merged
merged 5 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions API_changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ API changes 3.8.0
- Remove skip_encode parameter.
- rename ModbusExceptions enums to legal constants.
- enforce client keyword only parameters (positional not allowed).
- added trace_packet/pdu/connect to client
- removed on_connect_callback from client
- added trace_packet/pdu/connect to client/server.
- removed on_connect_callback from client.
- removed response_manipulator, request_tracer from server.

API changes 3.7.0
-----------------
Expand Down
46 changes: 21 additions & 25 deletions examples/server_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
ModbusServerContext,
ModbusSlaveContext,
)
from pymodbus.pdu import ModbusPDU
from pymodbus.server import ModbusTcpServer


Expand All @@ -24,29 +25,23 @@ class Manipulator:
message_count: int = 1
server: ModbusTcpServer

def server_request_tracer(self, request, *_addr):
"""Trace requests.
def trace_packet(self, sending: bool, data: bytes) -> bytes:
"""Do dummy trace."""
txt = "REQUEST stream" if sending else "RESPONSE stream"
print(f"---> {txt}: {data!r}")

All server requests passes this filter before being handled.
"""
print(f"---> REQUEST: {request}")
return data

def server_response_manipulator(self, response):
"""Manipulate responses.
def trace_pdu(self, sending: bool, pdu: ModbusPDU) -> ModbusPDU:
"""Do dummy trace."""
txt = "REQUEST pdu" if sending else "RESPONSE pdu"
print(f"---> {txt}: {pdu}")
return pdu

All server responses passes this filter before being sent.
The filter returns:

- response, either original or modified
- skip_encoding, signals whether or not to encode the response
"""
if not self.message_count:
print(f"---> RESPONSE: {response}")
self.message_count = 3
else:
print("---> RESPONSE: NONE")
self.message_count -= 1
return response, False
def trace_connect(self, connect: bool) -> None:
"""Do dummy trace."""
txt = "Connected" if connect else "Disconnected"
print(f"---> {txt}")

async def setup(self):
"""Prepare server."""
Expand All @@ -60,11 +55,12 @@ async def setup(self):
)
self.server = ModbusTcpServer(
context,
FramerType.SOCKET,
None,
("127.0.0.1", 5020),
request_tracer=self.server_request_tracer,
response_manipulator=self.server_response_manipulator,
framer=FramerType.SOCKET,
identity=None,
address=("127.0.0.1", 5020),
trace_packet=self.trace_packet,
trace_pdu=self.trace_pdu,
trace_connect=self.trace_connect,
)

async def run(self):
Expand Down
26 changes: 13 additions & 13 deletions pymodbus/client/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ def __init__(
framer: FramerType,
retries: int,
comm_params: CommParams,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None,
trace_connect: Callable[[bool], None] | None,
) -> None:
"""Initialize a client instance.

Expand All @@ -40,9 +40,9 @@ def __init__(
(FRAMER_NAME_TO_CLASS[framer])(DecodePDU(False)),
retries,
False,
trace_packet=trace_packet,
trace_pdu=trace_pdu,
trace_connect=trace_connect,
trace_packet,
trace_pdu,
trace_connect,
)
self.state = ModbusTransactionState.IDLE

Expand Down Expand Up @@ -119,9 +119,9 @@ def __init__(
framer: FramerType,
retries: int,
comm_params: CommParams,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None,
trace_connect: Callable[[bool], None] | None,
) -> None:
"""Initialize a client instance.

Expand All @@ -139,9 +139,9 @@ def __init__(
self.framer,
retries,
False,
trace_packet=trace_packet,
trace_pdu=trace_pdu,
trace_connect=trace_connect,
trace_packet,
trace_pdu,
trace_connect,
sync_client=self,
)
self.reconnect_delay_current = self.comm_params.reconnect_delay or 0
Expand Down Expand Up @@ -202,7 +202,7 @@ def _start_send(self):
self.state = ModbusTransactionState.SENDING

@abstractmethod
def send(self, request: bytes) -> int:
def send(self, request: bytes, addr: tuple | None = None) -> int:
"""Send request.

:meta private:
Expand Down
20 changes: 12 additions & 8 deletions pymodbus/client/serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max: float = 300,
timeout: float = 3,
retries: int = 3,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
) -> None:
"""Initialize Asyncio Modbus Serial Client."""
Expand Down Expand Up @@ -111,6 +111,9 @@ def __init__( # pylint: disable=too-many-arguments
framer,
retries,
self.comm_params,
trace_packet,
trace_pdu,
trace_connect,
)


Expand Down Expand Up @@ -174,8 +177,8 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max: float = 300,
timeout: float = 3,
retries: int = 3,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
) -> None:
"""Initialize Modbus Serial Client."""
Expand Down Expand Up @@ -203,9 +206,9 @@ def __init__( # pylint: disable=too-many-arguments
framer,
retries,
self.comm_params,
trace_connect=trace_connect,
trace_packet=trace_packet,
trace_pdu=trace_pdu,
trace_packet,
trace_pdu,
trace_connect,
)
self.socket: serial.Serial | None = None
self.last_frame_end = None
Expand Down Expand Up @@ -280,8 +283,9 @@ def _send(self, request: bytes) -> int: # pragma: no cover
return size
return 0

def send(self, request: bytes) -> int: # pragma: no cover
def send(self, request: bytes, addr: tuple | None = None) -> int: # pragma: no cover
"""Send data on the underlying socket."""
_ = addr
start = time.time()
if hasattr(self,"ctx"):
timeout = start + self.ctx.comm_params.timeout_connect
Expand Down
23 changes: 17 additions & 6 deletions pymodbus/client/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max: float = 300,
timeout: float = 3,
retries: int = 3,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
) -> None:
"""Initialize Asyncio Modbus TCP Client."""
Expand All @@ -93,6 +93,9 @@ def __init__( # pylint: disable=too-many-arguments
framer,
retries,
self.comm_params,
trace_packet,
trace_pdu,
trace_connect,
)


Expand Down Expand Up @@ -148,8 +151,8 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max: float = 300,
timeout: float = 3,
retries: int = 3,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
) -> None:
"""Initialize Modbus TCP Client."""
Expand All @@ -166,7 +169,14 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max=reconnect_delay_max,
timeout_connect=timeout,
)
super().__init__(framer, retries, self.comm_params)
super().__init__(
framer,
retries,
self.comm_params,
trace_packet,
trace_pdu,
trace_connect,
)
self.socket = None

@property
Expand Down Expand Up @@ -204,8 +214,9 @@ def close(self):
self.socket.close()
self.socket = None

def send(self, request):
def send(self, request, addr: tuple | None = None):
"""Send data on the underlying socket."""
_ = addr
super()._start_send()
if not self.socket:
raise ConnectionException(str(self))
Expand Down
14 changes: 10 additions & 4 deletions pymodbus/client/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max: float = 300,
timeout: float = 3,
retries: int = 3,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
):
"""Initialize Asyncio Modbus TLS Client."""
Expand All @@ -92,6 +92,9 @@ def __init__( # pylint: disable=too-many-arguments
"",
framer=framer,
retries=retries,
trace_packet=trace_packet,
trace_pdu=trace_pdu,
trace_connect=trace_connect,
)

@classmethod
Expand Down Expand Up @@ -168,8 +171,8 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max: float = 300,
timeout: float = 3,
retries: int = 3,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
):
"""Initialize Modbus TLS Client."""
Expand All @@ -190,6 +193,9 @@ def __init__( # pylint: disable=too-many-arguments
"",
framer=framer,
retries=retries,
trace_packet=trace_packet,
trace_pdu=trace_pdu,
trace_connect=trace_connect,
)

@classmethod
Expand Down
23 changes: 17 additions & 6 deletions pymodbus/client/udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max: float = 300,
timeout: float = 3,
retries: int = 3,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
) -> None:
"""Initialize Asyncio Modbus UDP Client."""
Expand All @@ -93,6 +93,9 @@ def __init__( # pylint: disable=too-many-arguments
framer,
retries,
self.comm_params,
trace_packet,
trace_pdu,
trace_connect,
)
self.source_address = source_address

Expand Down Expand Up @@ -149,8 +152,8 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max: float = 300,
timeout: float = 3,
retries: int = 3,
trace_packet: Callable[[bool, bytes | None], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU | None], ModbusPDU] | None = None,
trace_packet: Callable[[bool, bytes], bytes] | None = None,
trace_pdu: Callable[[bool, ModbusPDU], ModbusPDU] | None = None,
trace_connect: Callable[[bool], None] | None = None,
) -> None:
"""Initialize Modbus UDP Client."""
Expand All @@ -166,7 +169,14 @@ def __init__( # pylint: disable=too-many-arguments
reconnect_delay_max=reconnect_delay_max,
timeout_connect=timeout,
)
super().__init__(framer, retries, self.comm_params)
super().__init__(
framer,
retries,
self.comm_params,
trace_packet,
trace_pdu,
trace_connect,
)
self.socket = None

@property
Expand Down Expand Up @@ -196,11 +206,12 @@ def close(self):
"""
self.socket = None

def send(self, request: bytes) -> int:
def send(self, request: bytes, addr: tuple | None = None) -> int:
"""Send data on the underlying socket.

:meta private:
"""
_ = addr
super()._start_send()
if not self.socket:
raise ConnectionException(str(self))
Expand Down
Loading