188 lines
6.7 KiB
GDScript
188 lines
6.7 KiB
GDScript
extends Node
|
||
|
||
## Contains base classes for modbus request/response/error packets
|
||
|
||
# В автозагрузке должно быть
|
||
# from pymodbus.exceptions import
|
||
# from pymodbus.constants import Defaults
|
||
# from pymodbus.utilities import rtuFrameSize
|
||
|
||
|
||
# Base PDU's
|
||
class ModbusPDU extends Object:
|
||
## Base class for all Modbus messages
|
||
## [param transaction_id]
|
||
## This value is used to uniquely identify a request
|
||
## response pair. It can be implemented as a simple counter
|
||
## [param protocol_id]
|
||
## This is a constant set at 0 to indicate Modbus. It is
|
||
## put here for ease of expansion.
|
||
## [param unit_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).
|
||
## [param check]
|
||
## This is used for LRC/CRC in the serial modbus protocols
|
||
## [param 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.
|
||
|
||
var transaction_id
|
||
var protocol_id
|
||
var unit_id
|
||
var skip_encode
|
||
var check
|
||
var function_code = 0
|
||
var exception_code = 0
|
||
|
||
## Initializes the base data for a modbus request
|
||
func _init(kwargs: Dictionary):
|
||
transaction_id = kwargs.get('transaction', constants.Defaults.TransactionId)
|
||
protocol_id = kwargs.get('protocol', constants.Defaults.ProtocolId)
|
||
unit_id = kwargs.get('unit', constants.Defaults.UnitId)
|
||
skip_encode = kwargs.get('skip_encode', false)
|
||
check = 0x0000
|
||
|
||
|
||
## Encodes the message
|
||
## [b]raises[/b] A not implemented exception
|
||
func encode():
|
||
exceptions.NotImplementedException.new().raise()
|
||
|
||
## Decodes data part of the message.
|
||
## [param data] is a string object
|
||
## [b]raises[/b] A not implemented exception
|
||
func decode(data: PackedByteArray):
|
||
exceptions.NotImplementedException.new().raise()
|
||
|
||
## Calculates the size of a PDU.
|
||
## [param buffer] A buffer containing the data that have been received.
|
||
## [b]returns[/b] The number of bytes in the PDU.
|
||
func calculateRtuFrameSize(buffer: PackedByteArray):
|
||
var rc = self.get('_rtu_frame_size')
|
||
if rc != null:
|
||
return rc
|
||
rc = self.get('_rtu_byte_count_pos')
|
||
if rc != null:
|
||
return utilities.rtuFrameSize(buffer, rc)
|
||
else:
|
||
exceptions.NotImplementedException.new('Cannot determine RTU frame size for %s' % self).raise()
|
||
|
||
|
||
## Base class for a modbus request PDU
|
||
class ModbusRequest extends ModbusPDU:
|
||
|
||
func _init(kwargs: Dictionary):
|
||
# Proxy to the lower level initializer
|
||
super(kwargs)
|
||
|
||
## Builds an error response based on the function[br]
|
||
## [param exception] The exception to return[br]
|
||
## [b]raises[/b] An exception response[br]
|
||
func doException(exception):
|
||
var exc = exceptions.ExceptionResponse(function_code, exception)
|
||
log.error(exc)
|
||
return exc
|
||
|
||
|
||
class ModbusResponse extends ModbusPDU:
|
||
## Base class for a modbus response PDU
|
||
## [param should_respond]
|
||
## A flag that indicates if this response returns a result back
|
||
## to the client issuing the request
|
||
## [param _rtu_frame_size]
|
||
## Indicates the size of the modbus rtu response used for
|
||
## calculating how much to read.
|
||
var should_respond = true
|
||
|
||
## Proxy to the lower level initializer
|
||
func _init(kwargs: Dictionary):
|
||
super(kwargs)
|
||
|
||
## Checks if the error is a success or failure
|
||
func isError():
|
||
return function_code > 0x80
|
||
|
||
|
||
|
||
## Exception PDU's. An enumeration of the valid modbus exceptions
|
||
class ModbusExceptions:
|
||
const IllegalFunction = 0x01
|
||
const IllegalAddress = 0x02
|
||
const IllegalValue = 0x03
|
||
const SlaveFailure = 0x04
|
||
const Acknowledge = 0x05
|
||
const SlaveBusy = 0x06
|
||
const MemoryParityError = 0x08
|
||
const GatewayPathUnavailable = 0x0A
|
||
const GatewayNoResponse = 0x0B
|
||
|
||
var _decoded: = {}
|
||
|
||
func _init():
|
||
for prop in get_property_list():
|
||
_decoded[get(prop.name)] = prop.name
|
||
|
||
## Given an error code, translate it to a string error name.[br]
|
||
## [param code] The code number to translate
|
||
func decode(code: int) -> String: return _decoded.get(code)
|
||
|
||
|
||
## Base class for a modbus exception PDU
|
||
class ExceptionResponse extends ModbusResponse:
|
||
var ExceptionOffset = 0x80
|
||
var _rtu_frame_size = 5
|
||
var original_code = function_code
|
||
|
||
## Initializes the modbus exception response
|
||
## [param function_code] The function to build an exception response for
|
||
## [param exception_code] The specific modbus exception to return
|
||
func _init(func_code, kwargs: Dictionary, except_code: int = 0):
|
||
super(kwargs)
|
||
function_code = func_code | ExceptionOffset
|
||
exception_code = except_code
|
||
original_code = function_code
|
||
|
||
## Encodes a modbus exception response
|
||
func encode(): return exception_code
|
||
|
||
## Decodes a modbus exception response[br]
|
||
## [param data] The packet data to decode
|
||
func decode(data: PackedByteArray):
|
||
exception_code = data.decode_u8(0)
|
||
|
||
## Builds a representation of an exception response[/br]
|
||
## [b]returns[/b] The string representation of an exception response
|
||
func _to_string() -> String:
|
||
var message = ModbusExceptions.new().decode(self.exception_code)
|
||
return 'Exception Response(%d, %d, %s)' % [function_code, original_code, message]
|
||
|
||
|
||
## Defines 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
|
||
class IllegalFunctionRequest extends ModbusRequest:
|
||
var ErrorCode = 1
|
||
|
||
## Initializes a IllegalFunctionRequest[br]
|
||
## [param function_code] The function we are erroring on[br]
|
||
func _init(func_code, kwargs):
|
||
super(kwargs)
|
||
self.function_code = func_code
|
||
|
||
## This is here so this failure will run correctly[br]
|
||
## [param data] Not used[br]
|
||
func decode(_data: PackedByteArray): pass
|
||
|
||
## Builds an illegal function request error response
|
||
## [param context] The current context for the message
|
||
## [b]returns[/b] The error response packet
|
||
func execute(context):
|
||
return ExceptionResponse.new(function_code, ErrorCode)
|