venv added, updated
This commit is contained in:
833
myenv/lib/python3.12/site-packages/pymodbus/pdu/diag_message.py
Normal file
833
myenv/lib/python3.12/site-packages/pymodbus/pdu/diag_message.py
Normal file
@@ -0,0 +1,833 @@
|
||||
"""Diagnostic Record Read/Write.
|
||||
|
||||
These need to be tied into a the current server context
|
||||
or linked to the appropriate data
|
||||
"""
|
||||
|
||||
|
||||
# pylint: disable=missing-type-doc
|
||||
import struct
|
||||
|
||||
from pymodbus.constants import ModbusPlusOperation, ModbusStatus
|
||||
from pymodbus.device import ModbusControlBlock
|
||||
from pymodbus.exceptions import ModbusException, NotImplementedException
|
||||
from pymodbus.pdu import ModbusRequest, ModbusResponse
|
||||
from pymodbus.utilities import pack_bitstring
|
||||
|
||||
|
||||
_MCB = ModbusControlBlock()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Function Codes Base Classes
|
||||
# diagnostic 08, 00-18,20
|
||||
# ---------------------------------------------------------------------------#
|
||||
# TODO Make sure all the data is decoded from the response # pylint: disable=fixme
|
||||
# ---------------------------------------------------------------------------#
|
||||
class DiagnosticStatusRequest(ModbusRequest):
|
||||
"""This is a base class for all of the diagnostic request functions."""
|
||||
|
||||
function_code = 0x08
|
||||
function_code_name = "diagnostic_status"
|
||||
_rtu_frame_size = 8
|
||||
|
||||
def __init__(self, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize a diagnostic request."""
|
||||
ModbusRequest.__init__(self, slave, transaction, protocol, skip_encode)
|
||||
self.message = None
|
||||
|
||||
def encode(self):
|
||||
"""Encode a diagnostic response.
|
||||
|
||||
we encode the data set in self.message
|
||||
|
||||
:returns: The encoded packet
|
||||
"""
|
||||
packet = struct.pack(">H", self.sub_function_code)
|
||||
if self.message is not None:
|
||||
if isinstance(self.message, str):
|
||||
packet += self.message.encode()
|
||||
elif isinstance(self.message, bytes):
|
||||
packet += self.message
|
||||
elif isinstance(self.message, (list, tuple)):
|
||||
for piece in self.message:
|
||||
packet += struct.pack(">H", piece)
|
||||
elif isinstance(self.message, int):
|
||||
packet += struct.pack(">H", self.message)
|
||||
return packet
|
||||
|
||||
def decode(self, data):
|
||||
"""Decode a diagnostic request.
|
||||
|
||||
:param data: The data to decode into the function code
|
||||
"""
|
||||
(
|
||||
self.sub_function_code, # pylint: disable=attribute-defined-outside-init
|
||||
) = struct.unpack(">H", data[:2])
|
||||
if self.sub_function_code == ReturnQueryDataRequest.sub_function_code:
|
||||
self.message = data[2:]
|
||||
else:
|
||||
(self.message,) = struct.unpack(">H", data[2:])
|
||||
|
||||
def get_response_pdu_size(self):
|
||||
"""Get response pdu size.
|
||||
|
||||
Func_code (1 byte) + Sub function code (2 byte) + Data (2 * N bytes)
|
||||
:return:
|
||||
"""
|
||||
if not isinstance(self.message, list):
|
||||
self.message = [self.message]
|
||||
return 1 + 2 + 2 * len(self.message)
|
||||
|
||||
|
||||
class DiagnosticStatusResponse(ModbusResponse):
|
||||
"""Diagnostic status.
|
||||
|
||||
This is a base class for all of the diagnostic response functions
|
||||
|
||||
It works by performing all of the encoding and decoding of variable
|
||||
data and lets the higher classes define what extra data to append
|
||||
and how to execute a request
|
||||
"""
|
||||
|
||||
function_code = 0x08
|
||||
_rtu_frame_size = 8
|
||||
|
||||
def __init__(self, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize a diagnostic response."""
|
||||
ModbusResponse.__init__(self, slave, transaction, protocol, skip_encode)
|
||||
self.message = None
|
||||
|
||||
def encode(self):
|
||||
"""Encode diagnostic response.
|
||||
|
||||
we encode the data set in self.message
|
||||
|
||||
:returns: The encoded packet
|
||||
"""
|
||||
packet = struct.pack(">H", self.sub_function_code)
|
||||
if self.message is not None:
|
||||
if isinstance(self.message, str):
|
||||
packet += self.message.encode()
|
||||
elif isinstance(self.message, bytes):
|
||||
packet += self.message
|
||||
elif isinstance(self.message, (list, tuple)):
|
||||
for piece in self.message:
|
||||
packet += struct.pack(">H", piece)
|
||||
elif isinstance(self.message, int):
|
||||
packet += struct.pack(">H", self.message)
|
||||
return packet
|
||||
|
||||
def decode(self, data):
|
||||
"""Decode diagnostic response.
|
||||
|
||||
:param data: The data to decode into the function code
|
||||
"""
|
||||
(
|
||||
self.sub_function_code, # pylint: disable=attribute-defined-outside-init
|
||||
) = struct.unpack(">H", data[:2])
|
||||
data = data[2:]
|
||||
if self.sub_function_code == ReturnQueryDataRequest.sub_function_code:
|
||||
self.message = data
|
||||
else:
|
||||
word_len = len(data) // 2
|
||||
if len(data) % 2:
|
||||
word_len += 1
|
||||
data += b"0"
|
||||
data = struct.unpack(">" + "H" * word_len, data)
|
||||
self.message = data
|
||||
|
||||
|
||||
class DiagnosticStatusSimpleRequest(DiagnosticStatusRequest):
|
||||
"""Return diagnostic status.
|
||||
|
||||
A large majority of the diagnostic functions are simple
|
||||
status request functions. They work by sending 0x0000
|
||||
as data and their function code and they are returned
|
||||
2 bytes of data.
|
||||
|
||||
If a function inherits this, they only need to implement
|
||||
the execute method
|
||||
"""
|
||||
|
||||
def __init__(self, data=0x0000, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize a simple diagnostic request.
|
||||
|
||||
The data defaults to 0x0000 if not provided as over half
|
||||
of the functions require it.
|
||||
|
||||
:param data: The data to send along with the request
|
||||
"""
|
||||
DiagnosticStatusRequest.__init__(self, slave=slave, transaction=transaction, protocol=protocol, skip_encode=skip_encode)
|
||||
self.message = data
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Raise if not implemented."""
|
||||
raise NotImplementedException("Diagnostic Message Has No Execute Method")
|
||||
|
||||
|
||||
class DiagnosticStatusSimpleResponse(DiagnosticStatusResponse):
|
||||
"""Diagnostic status.
|
||||
|
||||
A large majority of the diagnostic functions are simple
|
||||
status request functions. They work by sending 0x0000
|
||||
as data and their function code and they are returned
|
||||
2 bytes of data.
|
||||
"""
|
||||
|
||||
def __init__(self, data=0x0000, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Return a simple diagnostic response.
|
||||
|
||||
:param data: The resulting data to return to the client
|
||||
"""
|
||||
DiagnosticStatusResponse.__init__(self, slave=slave, transaction=transaction, protocol=protocol, skip_encode=skip_encode)
|
||||
self.message = data
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 00
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnQueryDataRequest(DiagnosticStatusRequest):
|
||||
"""Return query data.
|
||||
|
||||
The data passed in the request data field is to be returned (looped back)
|
||||
in the response. The entire response message should be identical to the
|
||||
request.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0000
|
||||
|
||||
def __init__(self, message=b"\x00\x00", slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize a new instance of the request.
|
||||
|
||||
:param message: The message to send to loopback
|
||||
"""
|
||||
DiagnosticStatusRequest.__init__(self, slave=slave, transaction=transaction, protocol=protocol, skip_encode=skip_encode)
|
||||
if not isinstance(message, bytes):
|
||||
raise ModbusException(f"message({type(message)}) must be bytes")
|
||||
self.message = message
|
||||
|
||||
async def execute(self, *_args):
|
||||
"""Execute the loopback request (builds the response).
|
||||
|
||||
:returns: The populated loopback response message
|
||||
"""
|
||||
return ReturnQueryDataResponse(self.message)
|
||||
|
||||
|
||||
class ReturnQueryDataResponse(DiagnosticStatusResponse):
|
||||
"""Return query data.
|
||||
|
||||
The data passed in the request data field is to be returned (looped back)
|
||||
in the response. The entire response message should be identical to the
|
||||
request.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0000
|
||||
|
||||
def __init__(self, message=b"\x00\x00", slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize a new instance of the response.
|
||||
|
||||
:param message: The message to loopback
|
||||
"""
|
||||
DiagnosticStatusResponse.__init__(self, slave=slave, transaction=transaction, protocol=protocol, skip_encode=skip_encode)
|
||||
if not isinstance(message, bytes):
|
||||
raise ModbusException(f"message({type(message)}) must be bytes")
|
||||
self.message = message
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 01
|
||||
# ---------------------------------------------------------------------------#
|
||||
class RestartCommunicationsOptionRequest(DiagnosticStatusRequest):
|
||||
"""Restart communication.
|
||||
|
||||
The remote device serial line port must be initialized and restarted, and
|
||||
all of its communications event counters are cleared. If the port is
|
||||
currently in Listen Only Mode, no response is returned. This function is
|
||||
the only one that brings the port out of Listen Only Mode. If the port is
|
||||
not currently in Listen Only Mode, a normal response is returned. This
|
||||
occurs before the restart is executed.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0001
|
||||
|
||||
def __init__(self, toggle=False, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize a new request.
|
||||
|
||||
:param toggle: Set to True to toggle, False otherwise
|
||||
"""
|
||||
DiagnosticStatusRequest.__init__(self, slave=slave, transaction=transaction, protocol=protocol, skip_encode=skip_encode)
|
||||
if toggle:
|
||||
self.message = [ModbusStatus.ON]
|
||||
else:
|
||||
self.message = [ModbusStatus.OFF]
|
||||
|
||||
async def execute(self, *_args):
|
||||
"""Clear event log and restart.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
# if _MCB.ListenOnly:
|
||||
return RestartCommunicationsOptionResponse(self.message)
|
||||
|
||||
|
||||
class RestartCommunicationsOptionResponse(DiagnosticStatusResponse):
|
||||
"""Restart Communication.
|
||||
|
||||
The remote device serial line port must be initialized and restarted, and
|
||||
all of its communications event counters are cleared. If the port is
|
||||
currently in Listen Only Mode, no response is returned. This function is
|
||||
the only one that brings the port out of Listen Only Mode. If the port is
|
||||
not currently in Listen Only Mode, a normal response is returned. This
|
||||
occurs before the restart is executed.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0001
|
||||
|
||||
def __init__(self, toggle=False, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize a new response.
|
||||
|
||||
:param toggle: Set to True if we toggled, False otherwise
|
||||
"""
|
||||
DiagnosticStatusResponse.__init__(self, slave=slave, transaction=transaction, protocol=protocol, skip_encode=skip_encode)
|
||||
if toggle:
|
||||
self.message = [ModbusStatus.ON]
|
||||
else:
|
||||
self.message = [ModbusStatus.OFF]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 02
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnDiagnosticRegisterRequest(DiagnosticStatusSimpleRequest):
|
||||
"""The contents of the remote device's 16-bit diagnostic register are returned in the response."""
|
||||
|
||||
sub_function_code = 0x0002
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
# if _MCB.isListenOnly():
|
||||
register = pack_bitstring(_MCB.getDiagnosticRegister())
|
||||
return ReturnDiagnosticRegisterResponse(register)
|
||||
|
||||
|
||||
class ReturnDiagnosticRegisterResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return diagnostic register.
|
||||
|
||||
The contents of the remote device's 16-bit diagnostic register are
|
||||
returned in the response
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0002
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 03
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ChangeAsciiInputDelimiterRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Change ascii input delimiter.
|
||||
|
||||
The character "CHAR" passed in the request data field becomes the end of
|
||||
message delimiter for future messages (replacing the default LF
|
||||
character). This function is useful in cases of a Line Feed is not
|
||||
required at the end of ASCII messages.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0003
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
char = (self.message & 0xFF00) >> 8 # type: ignore[operator]
|
||||
_MCB.Delimiter = char
|
||||
return ChangeAsciiInputDelimiterResponse(self.message)
|
||||
|
||||
|
||||
class ChangeAsciiInputDelimiterResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Change ascii input delimiter.
|
||||
|
||||
The character "CHAR" passed in the request data field becomes the end of
|
||||
message delimiter for future messages (replacing the default LF
|
||||
character). This function is useful in cases of a Line Feed is not
|
||||
required at the end of ASCII messages.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0003
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 04
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ForceListenOnlyModeRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Forces the addressed remote device to its Listen Only Mode for MODBUS communications.
|
||||
|
||||
This isolates it from the other devices on the network,
|
||||
allowing them to continue communicating without interruption from the
|
||||
addressed remote device. No response is returned.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0004
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
_MCB.ListenOnly = True
|
||||
return ForceListenOnlyModeResponse()
|
||||
|
||||
|
||||
class ForceListenOnlyModeResponse(DiagnosticStatusResponse):
|
||||
"""Forces the addressed remote device to its Listen Only Mode for MODBUS communications.
|
||||
|
||||
This isolates it from the other devices on the network,
|
||||
allowing them to continue communicating without interruption from the
|
||||
addressed remote device. No response is returned.
|
||||
|
||||
This does not send a response
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0004
|
||||
should_respond = False
|
||||
|
||||
def __init__(self, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize to block a return response."""
|
||||
DiagnosticStatusResponse.__init__(self, slave=slave, transaction=transaction, protocol=protocol, skip_encode=skip_encode)
|
||||
self.message = []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 10
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ClearCountersRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Clear ll counters and the diagnostic register.
|
||||
|
||||
Also, counters are cleared upon power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000A
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
_MCB.reset()
|
||||
return ClearCountersResponse(self.message)
|
||||
|
||||
|
||||
class ClearCountersResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Clear ll counters and the diagnostic register.
|
||||
|
||||
Also, counters are cleared upon power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000A
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 11
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnBusMessageCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return bus message count.
|
||||
|
||||
The response data field returns the quantity of messages that the
|
||||
remote device has detected on the communications systems since its last
|
||||
restart, clear counters operation, or power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000B
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.BusMessage
|
||||
return ReturnBusMessageCountResponse(count)
|
||||
|
||||
|
||||
class ReturnBusMessageCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return bus message count.
|
||||
|
||||
The response data field returns the quantity of messages that the
|
||||
remote device has detected on the communications systems since its last
|
||||
restart, clear counters operation, or power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000B
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 12
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnBusCommunicationErrorCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return bus comm. count.
|
||||
|
||||
The response data field returns the quantity of CRC errors encountered
|
||||
by the remote device since its last restart, clear counter operation, or
|
||||
power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000C
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.BusCommunicationError
|
||||
return ReturnBusCommunicationErrorCountResponse(count)
|
||||
|
||||
|
||||
class ReturnBusCommunicationErrorCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return bus comm. error.
|
||||
|
||||
The response data field returns the quantity of CRC errors encountered
|
||||
by the remote device since its last restart, clear counter operation, or
|
||||
power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000C
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 13
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnBusExceptionErrorCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return bus exception.
|
||||
|
||||
The response data field returns the quantity of modbus exception
|
||||
responses returned by the remote device since its last restart,
|
||||
clear counters operation, or power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000D
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.BusExceptionError
|
||||
return ReturnBusExceptionErrorCountResponse(count)
|
||||
|
||||
|
||||
class ReturnBusExceptionErrorCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return bus exception.
|
||||
|
||||
The response data field returns the quantity of modbus exception
|
||||
responses returned by the remote device since its last restart,
|
||||
clear counters operation, or power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000D
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 14
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnSlaveMessageCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return slave message count.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device, or broadcast, that the remote device has processed since
|
||||
its last restart, clear counters operation, or power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000E
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.SlaveMessage
|
||||
return ReturnSlaveMessageCountResponse(count)
|
||||
|
||||
|
||||
class ReturnSlaveMessageCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return slave message count.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device, or broadcast, that the remote device has processed since
|
||||
its last restart, clear counters operation, or power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000E
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 15
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnSlaveNoResponseCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return slave no response.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device, or broadcast, that the remote device has processed since
|
||||
its last restart, clear counters operation, or power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000F
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.SlaveNoResponse
|
||||
return ReturnSlaveNoResponseCountResponse(count)
|
||||
|
||||
|
||||
class ReturnSlaveNoResponseCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return slave no response.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device, or broadcast, that the remote device has processed since
|
||||
its last restart, clear counters operation, or power-up
|
||||
"""
|
||||
|
||||
sub_function_code = 0x000F
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 16
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnSlaveNAKCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return slave NAK count.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device for which it returned a Negative Acknowledge (NAK) exception
|
||||
response, since its last restart, clear counters operation, or power-up.
|
||||
Exception responses are described and listed in section 7 .
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0010
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.SlaveNAK
|
||||
return ReturnSlaveNAKCountResponse(count)
|
||||
|
||||
|
||||
class ReturnSlaveNAKCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return slave NAK.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device for which it returned a Negative Acknowledge (NAK) exception
|
||||
response, since its last restart, clear counters operation, or power-up.
|
||||
Exception responses are described and listed in section 7.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0010
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 17
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnSlaveBusyCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return slave busy count.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device for which it returned a Slave Device Busy exception response,
|
||||
since its last restart, clear counters operation, or power-up.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0011
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.SlaveBusy
|
||||
return ReturnSlaveBusyCountResponse(count)
|
||||
|
||||
|
||||
class ReturnSlaveBusyCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return slave busy count.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device for which it returned a Slave Device Busy exception response,
|
||||
since its last restart, clear counters operation, or power-up.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0011
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 18
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnSlaveBusCharacterOverrunCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return slave character overrun.
|
||||
|
||||
The response data field returns the quantity of messages addressed to the
|
||||
remote device that it could not handle due to a character overrun condition,
|
||||
since its last restart, clear counters operation, or power-up. A character
|
||||
overrun is caused by data characters arriving at the port faster than they
|
||||
can be stored, or by the loss of a character due to a hardware malfunction.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0012
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.BusCharacterOverrun
|
||||
return ReturnSlaveBusCharacterOverrunCountResponse(count)
|
||||
|
||||
|
||||
class ReturnSlaveBusCharacterOverrunCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return the quantity of messages addressed to the remote device unhandled due to a character overrun.
|
||||
|
||||
Since its last restart, clear counters operation, or power-up. A character
|
||||
overrun is caused by data characters arriving at the port faster than they
|
||||
can be stored, or by the loss of a character due to a hardware malfunction.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0012
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 19
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ReturnIopOverrunCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Return IopOverrun.
|
||||
|
||||
An IOP overrun is caused by data characters arriving at the port
|
||||
faster than they can be stored, or by the loss of a character due
|
||||
to a hardware malfunction. This function is specific to the 884.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0013
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
count = _MCB.Counter.BusCharacterOverrun
|
||||
return ReturnIopOverrunCountResponse(count)
|
||||
|
||||
|
||||
class ReturnIopOverrunCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return Iop overrun count.
|
||||
|
||||
The response data field returns the quantity of messages
|
||||
addressed to the slave that it could not handle due to an 884
|
||||
IOP overrun condition, since its last restart, clear counters
|
||||
operation, or power-up.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0013
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 20
|
||||
# ---------------------------------------------------------------------------#
|
||||
class ClearOverrunCountRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Clear the overrun error counter and reset the error flag.
|
||||
|
||||
An error flag should be cleared, but nothing else in the
|
||||
specification mentions is, so it is ignored.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0014
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
_MCB.Counter.BusCharacterOverrun = 0x0000
|
||||
return ClearOverrunCountResponse(self.message)
|
||||
|
||||
|
||||
class ClearOverrunCountResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Clear the overrun error counter and reset the error flag."""
|
||||
|
||||
sub_function_code = 0x0014
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------#
|
||||
# Diagnostic Sub Code 21
|
||||
# ---------------------------------------------------------------------------#
|
||||
class GetClearModbusPlusRequest(DiagnosticStatusSimpleRequest):
|
||||
"""Get/Clear modbus plus request.
|
||||
|
||||
In addition to the Function code (08) and Subfunction code
|
||||
(00 15 hex) in the query, a two-byte Operation field is used
|
||||
to specify either a "Get Statistics" or a "Clear Statistics"
|
||||
operation. The two operations are exclusive - the "Get"
|
||||
operation cannot clear the statistics, and the "Clear"
|
||||
operation does not return statistics prior to clearing
|
||||
them. Statistics are also cleared on power-up of the slave
|
||||
device.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0015
|
||||
|
||||
def __init__(self, data=0, slave=1, transaction=0, protocol=0, skip_encode=False):
|
||||
"""Initialize."""
|
||||
super().__init__(slave=slave, transaction=transaction, protocol=protocol, skip_encode=skip_encode)
|
||||
self.message=data
|
||||
|
||||
def get_response_pdu_size(self):
|
||||
"""Return a series of 54 16-bit words (108 bytes) in the data field of the response.
|
||||
|
||||
This function differs from the usual two-byte length of the data field.
|
||||
The data contains the statistics for the Modbus Plus peer processor in the slave device.
|
||||
Func_code (1 byte) + Sub function code (2 byte) + Operation (2 byte) + Data (108 bytes)
|
||||
:return:
|
||||
"""
|
||||
if self.message == ModbusPlusOperation.GET_STATISTICS:
|
||||
data = 2 + 108 # byte count(2) + data (54*2)
|
||||
else:
|
||||
data = 0
|
||||
return 1 + 2 + 2 + 2 + data
|
||||
|
||||
async def execute(self, *args):
|
||||
"""Execute the diagnostic request on the given device.
|
||||
|
||||
:returns: The initialized response message
|
||||
"""
|
||||
message = None # the clear operation does not return info
|
||||
if self.message == ModbusPlusOperation.CLEAR_STATISTICS:
|
||||
_MCB.Plus.reset()
|
||||
message = self.message
|
||||
else:
|
||||
message = [self.message]
|
||||
message += _MCB.Plus.encode()
|
||||
return GetClearModbusPlusResponse(message)
|
||||
|
||||
def encode(self):
|
||||
"""Encode a diagnostic response.
|
||||
|
||||
we encode the data set in self.message
|
||||
|
||||
:returns: The encoded packet
|
||||
"""
|
||||
packet = struct.pack(">H", self.sub_function_code)
|
||||
packet += struct.pack(">H", self.message)
|
||||
return packet
|
||||
|
||||
|
||||
class GetClearModbusPlusResponse(DiagnosticStatusSimpleResponse):
|
||||
"""Return a series of 54 16-bit words (108 bytes) in the data field of the response.
|
||||
|
||||
This function differs from the usual two-byte length of the data field.
|
||||
The data contains the statistics for the Modbus Plus peer processor in the slave device.
|
||||
"""
|
||||
|
||||
sub_function_code = 0x0015
|
||||
Reference in New Issue
Block a user