M lazy-lock.json M lua/custom/configs/lspconfig.lua M lua/custom/init.lua A lua/custom/journal.lua A nvim_venv/bin/Activate.ps1 A nvim_venv/bin/activate A nvim_venv/bin/activate.csh A nvim_venv/bin/activate.fish
103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
"""Framer implementations.
|
|
|
|
The implementation is responsible for encoding/decoding requests/responses.
|
|
|
|
According to the selected type of modbus frame a prefix/suffix is added/removed
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from enum import Enum
|
|
|
|
from pymodbus.exceptions import ModbusIOException
|
|
from pymodbus.logging import Log
|
|
from pymodbus.pdu import DecodePDU, ModbusPDU
|
|
|
|
|
|
class FramerType(str, Enum):
|
|
"""Type of Modbus frame."""
|
|
|
|
ASCII = "ascii"
|
|
RTU = "rtu"
|
|
SOCKET = "socket"
|
|
TLS = "tls"
|
|
|
|
|
|
class FramerBase:
|
|
"""Intern base."""
|
|
|
|
EMPTY = b''
|
|
MIN_SIZE = 0
|
|
|
|
def __init__(
|
|
self,
|
|
decoder: DecodePDU,
|
|
) -> None:
|
|
"""Initialize a ADU (framer) instance."""
|
|
self.decoder = decoder
|
|
|
|
def decode(self, _data: bytes) -> tuple[int, int, int, bytes]:
|
|
"""Decode ADU.
|
|
|
|
returns:
|
|
used_len (int) or 0 to read more
|
|
dev_id,
|
|
tid,
|
|
modbus request/response (bytes)
|
|
"""
|
|
return 0, 0, 0, self.EMPTY
|
|
|
|
def encode(self, data: bytes, _dev_id: int, _tid: int) -> bytes:
|
|
"""Encode ADU.
|
|
|
|
returns:
|
|
modbus ADU (bytes)
|
|
"""
|
|
return data
|
|
|
|
def buildFrame(self, message: ModbusPDU) -> bytes:
|
|
"""Create a ready to send modbus packet.
|
|
|
|
:param message: The populated request/response to send
|
|
"""
|
|
data = message.function_code.to_bytes(1,'big') + message.encode()
|
|
frame = self.encode(data, message.dev_id, message.transaction_id)
|
|
return frame
|
|
|
|
def processIncomingFrame(self, data: bytes) -> tuple[int, ModbusPDU | None]:
|
|
"""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.
|
|
"""
|
|
used_len = 0
|
|
while True:
|
|
data_len, pdu = self._processIncomingFrame(data[used_len:])
|
|
used_len += data_len
|
|
if not data_len:
|
|
return used_len, None
|
|
if pdu:
|
|
return used_len, pdu
|
|
|
|
def _processIncomingFrame(self, data: bytes) -> tuple[int, ModbusPDU | None]:
|
|
"""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.
|
|
"""
|
|
Log.debug("Processing: {}", data, ":hex")
|
|
if not data:
|
|
return 0, None
|
|
used_len, dev_id, tid, frame_data = self.decode(data)
|
|
if not frame_data:
|
|
return used_len, None
|
|
if (result := self.decoder.decode(frame_data)) is None:
|
|
raise ModbusIOException("Unable to decode request")
|
|
result.dev_id = dev_id
|
|
result.transaction_id = tid
|
|
Log.debug("Frame advanced, resetting header!!")
|
|
return used_len, result
|