143 lines
4.3 KiB
Python
143 lines
4.3 KiB
Python
"""Framer start."""
|
|
# pylint: disable=missing-type-doc
|
|
from typing import Any, Dict, Union
|
|
|
|
from pymodbus.factory import ClientDecoder, ServerDecoder
|
|
from pymodbus.logging import Log
|
|
|
|
|
|
# Unit ID, Function Code
|
|
BYTE_ORDER = ">"
|
|
FRAME_HEADER = "BB"
|
|
|
|
# Transaction Id, Protocol ID, Length, Unit ID, Function Code
|
|
SOCKET_FRAME_HEADER = BYTE_ORDER + "HHH" + FRAME_HEADER
|
|
|
|
# Function Code
|
|
TLS_FRAME_HEADER = BYTE_ORDER + "B"
|
|
|
|
|
|
class ModbusFramer:
|
|
"""Base Framer class."""
|
|
|
|
name = ""
|
|
|
|
def __init__(
|
|
self,
|
|
decoder: Union[ClientDecoder, ServerDecoder],
|
|
client=None,
|
|
) -> None:
|
|
"""Initialize a new instance of the framer.
|
|
|
|
:param decoder: The decoder implementation to use
|
|
"""
|
|
self.decoder = decoder
|
|
self.client = client
|
|
self._header: Dict[str, Any] = {
|
|
"lrc": "0000",
|
|
"len": 0,
|
|
"uid": 0x00,
|
|
"tid": 0,
|
|
"pid": 0,
|
|
"crc": b"\x00\x00",
|
|
}
|
|
self._buffer = b""
|
|
|
|
def _validate_slave_id(self, slaves: list, single: bool) -> bool:
|
|
"""Validate if the received data is valid for the client.
|
|
|
|
:param slaves: list of slave id for which the transaction is valid
|
|
:param single: Set to true to treat this as a single context
|
|
:return:
|
|
"""
|
|
if single:
|
|
return True
|
|
if 0 in slaves or 0xFF in slaves:
|
|
# Handle Modbus TCP slave identifier (0x00 0r 0xFF)
|
|
# in asynchronous requests
|
|
return True
|
|
return self._header["uid"] in slaves
|
|
|
|
def sendPacket(self, message):
|
|
"""Send packets on the bus.
|
|
|
|
With 3.5char delay between frames
|
|
:param message: Message to be sent over the bus
|
|
:return:
|
|
"""
|
|
return self.client.send(message)
|
|
|
|
def recvPacket(self, size):
|
|
"""Receive packet from the bus.
|
|
|
|
With specified len
|
|
:param size: Number of bytes to read
|
|
:return:
|
|
"""
|
|
return self.client.recv(size)
|
|
|
|
def resetFrame(self):
|
|
"""Reset the entire message frame.
|
|
|
|
This allows us to skip ovver errors that may be in the stream.
|
|
It is hard to know if we are simply out of sync or if there is
|
|
an error in the stream as we have no way to check the start or
|
|
end of the message (python just doesn't have the resolution to
|
|
check for millisecond delays).
|
|
"""
|
|
Log.debug(
|
|
"Resetting frame - Current Frame in buffer - {}", self._buffer, ":hex"
|
|
)
|
|
self._buffer = b""
|
|
self._header = {
|
|
"lrc": "0000",
|
|
"crc": b"\x00\x00",
|
|
"len": 0,
|
|
"uid": 0x00,
|
|
"pid": 0,
|
|
"tid": 0,
|
|
}
|
|
|
|
def populateResult(self, result):
|
|
"""Populate the modbus result header.
|
|
|
|
The serial packets do not have any header information
|
|
that is copied.
|
|
|
|
:param result: The response packet
|
|
"""
|
|
result.slave_id = self._header.get("uid", 0)
|
|
result.transaction_id = self._header.get("tid", 0)
|
|
result.protocol_id = self._header.get("pid", 0)
|
|
|
|
def processIncomingPacket(self, data, callback, slave, **kwargs):
|
|
"""Process new packet pattern.
|
|
|
|
This takes in a new request packet, adds it to the current
|
|
packet stream, and performs framing on it. That is, checks
|
|
for complete messages, and once found, will process all that
|
|
exist. This handles the case when we read N + 1 or 1 // N
|
|
messages at a time instead of 1.
|
|
|
|
The processed and decoded messages are pushed to the callback
|
|
function to process and send.
|
|
|
|
:param data: The new packet data
|
|
:param callback: The function to send results to
|
|
:param slave: Process if slave id matches, ignore otherwise (could be a
|
|
list of slave ids (server) or single slave id(client/server))
|
|
:param kwargs:
|
|
:raises ModbusIOException:
|
|
"""
|
|
Log.debug("Processing: {}", data, ":hex")
|
|
self._buffer += data
|
|
if not isinstance(slave, (list, tuple)):
|
|
slave = [slave]
|
|
single = kwargs.pop("single", False)
|
|
self.frameProcessIncomingPacket(single, callback, slave, **kwargs)
|
|
|
|
def frameProcessIncomingPacket(
|
|
self, _single, _callback, _slave, _tid=None, **kwargs
|
|
):
|
|
"""Process new packet pattern."""
|