venv added, updated
This commit is contained in:
255
myenv/lib/python3.12/site-packages/pymodbus/pdu/pdu.py
Normal file
255
myenv/lib/python3.12/site-packages/pymodbus/pdu/pdu.py
Normal file
@@ -0,0 +1,255 @@
|
||||
"""Contains base classes for modbus request/response/error packets."""
|
||||
|
||||
|
||||
# pylint: disable=missing-type-doc
|
||||
import struct
|
||||
|
||||
from pymodbus.exceptions import NotImplementedException
|
||||
from pymodbus.logging import Log
|
||||
from pymodbus.utilities import rtuFrameSize
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Base PDUs
|
||||
# --------------------------------------------------------------------------- #
|
||||
class ModbusPDU:
|
||||
"""Base class for all Modbus messages.
|
||||
|
||||
.. attribute:: transaction_id
|
||||
|
||||
This value is used to uniquely identify a request
|
||||
response pair. It can be implemented as a simple counter
|
||||
|
||||
.. attribute:: protocol_id
|
||||
|
||||
This is a constant set at 0 to indicate Modbus. It is
|
||||
put here for ease of expansion.
|
||||
|
||||
.. attribute:: slave_id
|
||||
|
||||
This is used to route the request to the correct child. In
|
||||
the TCP modbus, it is used for routing (or not used at all. However,
|
||||
for the serial versions, it is used to specify which child to perform
|
||||
the requests against. The value 0x00 represents the broadcast address
|
||||
(also 0xff).
|
||||
|
||||
.. attribute:: check
|
||||
|
||||
This is used for LRC/CRC in the serial modbus protocols
|
||||
|
||||
.. attribute:: skip_encode
|
||||
|
||||
This is used when the message payload has already been encoded.
|
||||
Generally this will occur when the PayloadBuilder is being used
|
||||
to create a complicated message. By setting this to True, the
|
||||
request will pass the currently encoded message through instead
|
||||
of encoding it again.
|
||||
"""
|
||||
|
||||
def __init__(self, slave, transaction, protocol, skip_encode):
|
||||
"""Initialize the base data for a modbus request.
|
||||
|
||||
:param slave: Modbus slave slave ID
|
||||
|
||||
"""
|
||||
self.transaction_id = transaction
|
||||
self.protocol_id = protocol
|
||||
self.slave_id = slave
|
||||
self.skip_encode = skip_encode
|
||||
self.check = 0x0000
|
||||
|
||||
def encode(self):
|
||||
"""Encode the message.
|
||||
|
||||
:raises: A not implemented exception
|
||||
"""
|
||||
raise NotImplementedException()
|
||||
|
||||
def decode(self, data):
|
||||
"""Decode data part of the message.
|
||||
|
||||
:param data: is a string object
|
||||
:raises NotImplementedException:
|
||||
"""
|
||||
raise NotImplementedException()
|
||||
|
||||
@classmethod
|
||||
def calculateRtuFrameSize(cls, buffer):
|
||||
"""Calculate the size of a PDU.
|
||||
|
||||
:param buffer: A buffer containing the data that have been received.
|
||||
:returns: The number of bytes in the PDU.
|
||||
:raises NotImplementedException:
|
||||
"""
|
||||
if hasattr(cls, "_rtu_frame_size"):
|
||||
return cls._rtu_frame_size
|
||||
if hasattr(cls, "_rtu_byte_count_pos"):
|
||||
return rtuFrameSize(buffer, cls._rtu_byte_count_pos)
|
||||
raise NotImplementedException(
|
||||
f"Cannot determine RTU frame size for {cls.__name__}"
|
||||
)
|
||||
|
||||
|
||||
class ModbusRequest(ModbusPDU):
|
||||
"""Base class for a modbus request PDU."""
|
||||
|
||||
function_code = -1
|
||||
|
||||
def __init__(self, slave, transaction, protocol, skip_encode):
|
||||
"""Proxy to the lower level initializer.
|
||||
|
||||
:param slave: Modbus slave slave ID
|
||||
"""
|
||||
super().__init__(slave, transaction, protocol, skip_encode)
|
||||
self.fut = None
|
||||
|
||||
def doException(self, exception):
|
||||
"""Build an error response based on the function.
|
||||
|
||||
:param exception: The exception to return
|
||||
:raises: An exception response
|
||||
"""
|
||||
exc = ExceptionResponse(self.function_code, exception)
|
||||
Log.error("Exception response {}", exc)
|
||||
return exc
|
||||
|
||||
|
||||
class ModbusResponse(ModbusPDU):
|
||||
"""Base class for a modbus response PDU.
|
||||
|
||||
.. attribute:: should_respond
|
||||
|
||||
A flag that indicates if this response returns a result back
|
||||
to the client issuing the request
|
||||
|
||||
.. attribute:: _rtu_frame_size
|
||||
|
||||
Indicates the size of the modbus rtu response used for
|
||||
calculating how much to read.
|
||||
"""
|
||||
|
||||
should_respond = True
|
||||
function_code = 0x00
|
||||
|
||||
def __init__(self, slave, transaction, protocol, skip_encode):
|
||||
"""Proxy the lower level initializer.
|
||||
|
||||
:param slave: Modbus slave slave ID
|
||||
|
||||
"""
|
||||
super().__init__(slave, transaction, protocol, skip_encode)
|
||||
self.bits = []
|
||||
self.registers = []
|
||||
self.request = None
|
||||
|
||||
def isError(self) -> bool:
|
||||
"""Check if the error is a success or failure."""
|
||||
return self.function_code > 0x80
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Exception PDUs
|
||||
# --------------------------------------------------------------------------- #
|
||||
class ModbusExceptions: # pylint: disable=too-few-public-methods
|
||||
"""An enumeration of the valid modbus exceptions."""
|
||||
|
||||
IllegalFunction = 0x01
|
||||
IllegalAddress = 0x02
|
||||
IllegalValue = 0x03
|
||||
SlaveFailure = 0x04
|
||||
Acknowledge = 0x05
|
||||
SlaveBusy = 0x06
|
||||
NegativeAcknowledge = 0x07
|
||||
MemoryParityError = 0x08
|
||||
GatewayPathUnavailable = 0x0A
|
||||
GatewayNoResponse = 0x0B
|
||||
|
||||
@classmethod
|
||||
def decode(cls, code):
|
||||
"""Give an error code, translate it to a string error name.
|
||||
|
||||
:param code: The code number to translate
|
||||
"""
|
||||
values = {
|
||||
v: k
|
||||
for k, v in iter(cls.__dict__.items())
|
||||
if not k.startswith("__") and not callable(v)
|
||||
}
|
||||
return values.get(code, None)
|
||||
|
||||
|
||||
class ExceptionResponse(ModbusResponse):
|
||||
"""Base class for a modbus exception PDU."""
|
||||
|
||||
ExceptionOffset = 0x80
|
||||
_rtu_frame_size = 5
|
||||
|
||||
def __init__(self, function_code, exception_code=None, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize the modbus exception response.
|
||||
|
||||
:param function_code: The function to build an exception response for
|
||||
:param exception_code: The specific modbus exception to return
|
||||
"""
|
||||
super().__init__(slave, transaction, protocol, skip_encode)
|
||||
self.original_code = function_code
|
||||
self.function_code = function_code | self.ExceptionOffset
|
||||
self.exception_code = exception_code
|
||||
|
||||
def encode(self):
|
||||
"""Encode a modbus exception response.
|
||||
|
||||
:returns: The encoded exception packet
|
||||
"""
|
||||
return struct.pack(">B", self.exception_code)
|
||||
|
||||
def decode(self, data):
|
||||
"""Decode a modbus exception response.
|
||||
|
||||
:param data: The packet data to decode
|
||||
"""
|
||||
self.exception_code = int(data[0])
|
||||
|
||||
def __str__(self):
|
||||
"""Build a representation of an exception response.
|
||||
|
||||
:returns: The string representation of an exception response
|
||||
"""
|
||||
message = ModbusExceptions.decode(self.exception_code)
|
||||
parameters = (self.function_code, self.original_code, message)
|
||||
return (
|
||||
"Exception Response(%d, %d, %s)" # pylint: disable=consider-using-f-string
|
||||
% parameters
|
||||
)
|
||||
|
||||
|
||||
class IllegalFunctionRequest(ModbusRequest):
|
||||
"""Define the Modbus slave exception type "Illegal Function".
|
||||
|
||||
This exception code is returned if the slave::
|
||||
|
||||
- does not implement the function code **or**
|
||||
- is not in a state that allows it to process the function
|
||||
"""
|
||||
|
||||
ErrorCode = 1
|
||||
|
||||
def __init__(self, function_code, xslave, xtransaction, xprotocol, xskip_encode):
|
||||
"""Initialize a IllegalFunctionRequest.
|
||||
|
||||
:param function_code: The function we are erroring on
|
||||
"""
|
||||
super().__init__(xslave, xtransaction, xprotocol, xskip_encode)
|
||||
self.function_code = function_code
|
||||
|
||||
def decode(self, _data):
|
||||
"""Decode so this failure will run correctly."""
|
||||
|
||||
def encode(self):
|
||||
"""Decode so this failure will run correctly."""
|
||||
|
||||
async def execute(self, _context):
|
||||
"""Build an illegal function request error response.
|
||||
|
||||
:returns: The error response packet
|
||||
"""
|
||||
return ExceptionResponse(self.function_code, self.ErrorCode)
|
||||
Reference in New Issue
Block a user