Files
uarep-ctl/modbus/pymodbus/utilities.gd

178 lines
5.4 KiB
GDScript

extends Node
## Modbus Utilities.
## A collection of utilities for packing data, unpacking
## data computing checksums, and decode checksums.
## Modbus Client States
class ModbusTransactionState extends Object:
const IDLE: = 0
const SENDING: = 1
const WAITING_FOR_REPLY: = 2
const WAITING_TURNAROUND_DELAY: = 3
const PROCESSING_REPLY: = 4
const PROCESSING_ERROR: = 5
const TRANSACTION_COMPLETE: = 6
const RETRYING: = 7
const NO_RESPONSE_STATE: = 8
func state_to_string(state) -> String:
var states: = {
ModbusTransactionState.IDLE: 'IDLE',
ModbusTransactionState.SENDING: 'SENDING',
ModbusTransactionState.WAITING_FOR_REPLY: 'WAITING_FOR_REPLY',
ModbusTransactionState.WAITING_TURNAROUND_DELAY: 'WAITING_TURNAROUND_DELAY',
ModbusTransactionState.PROCESSING_REPLY: 'PROCESSING_REPLY',
ModbusTransactionState.PROCESSING_ERROR: 'PROCESSING_ERROR',
ModbusTransactionState.TRANSACTION_COMPLETE: 'TRANSACTION_COMPLETE',
ModbusTransactionState.RETRYING: 'RETRYING TRANSACTION' }
return states.get(state, null)
var __crc16_table: = []
func _ready():
__crc16_table = __generate_crc16_table()
## Creates a string out of an array of bits
## [code]
## var bits = [False, True, False, True]
## var result = pack_bitstring(bits)
## [/code]
func pack_bitstring(bits: Array):
var ret = PackedByteArray()
var i = 0
var packed: int = 0
for bit in bits:
if bit:
packed += 128
i += 1
if i == 8:
ret += packed
i = 0
packed = 0
else:
packed >>= 1
if 0 < i < 8:
packed >>= (7 - i)
ret += packed
return ret
## Creates bit array out of a string
## param string: The modbus data packet to decode
## [code]
## var bytes = 'bytes to decode'
## var result = unpack_bitstring(bytes)
## [/code]
func unpack_bitstring(string: String):
var psa: = string.to_ascii_buffer()
var byte_count = psa.size()
var bits: = []
for byte in byte_count:
var value = psa.decode_u8(byte)
for i in 8:
bits.append((value & (1 << i)) > 0)
return bits
## Returns byte string from a given string
## [param s]
func make_byte_string(s) -> PackedByteArray:
return PackedByteArray(s)
# Error Detection Functions
func __generate_crc16_table() -> Array:
## Generates a crc16 lookup table
## note:: This will only be generated once
var result: = []
for byte in 256:
var crc = 0x0000
for i in 8:
if (byte ^ crc) & 0x0001:
crc = (crc >> 1) ^ 0xa001
else:
crc >>= 1
byte >>= 1
result.append(crc)
return result
## Computes a crc16 on the passed in string. For modbus,
## this is only used on the binary serial protocols (in this
## case RTU). The difference between modbus's crc16 and a normal crc16
## is that modbus starts the crc value out at 0xffff.
## [param data] The data to create a crc16 of
## [b]returns[/b] The calculated CRC
func computeCRC(data: PackedByteArray) -> int:
var crc: int = 0xffff
for a in data:
var idx = __crc16_table[(crc ^ a) & 0xff]
crc = ((crc >> 8) & 0xff) ^ idx
return ((crc << 8) & 0xff00) | ((crc >> 8) & 0x00ff)
## Checks if the data matches the passed in CRC
## [param data] The data to create a crc16 of
## [param check] The CRC to validate
## [b]returns[/b] True if matched, False otherwise
func checkCRC(data, check: int) -> bool:
return computeCRC(data) == check
## Used to compute the longitudinal redundancy check
## against a string. This is only used on the serial ASCII
## modbus protocol. A full description of this implementation
## can be found in appendex B of the serial line modbus description.
## [param data] The data to apply a lrc to
## [b]return[/b] The calculated LRC
func computeLRC(data: PackedByteArray) -> int:
var lrc: int = 0
for a in data:
lrc += a
lrc &= 0xff
lrc = (lrc ^ 0xff) + 1
return lrc & 0xff
## Checks if the passed in data matches the LRC
## [param data] The data to calculate
## [param check] The LRC to validate
## [b]returns[/b] True if matched, False otherwise
func checkLRC(data: PackedByteArray, check: int) -> bool:
return computeLRC(data) == check
## Calculates the size of the frame based on the byte count.
## param data: The buffer containing the frame.
## param byte_count_pos: The index of the byte count in the buffer.
## returns: The size of the frame.
##
## The structure of frames with a byte count field is always the
## same:
##
## first, there are some header fields
## then the byte count field
## then as many data bytes as indicated by the byte count,
## finally the CRC (two bytes).
##
## To calculate the frame size, it is therefore sufficient to extract
## the contents of the byte count field, add the position of this
## field, and finally increment the sum by three (one byte for the
## byte count field, two for the CRC).
func rtuFrameSize(data: PackedByteArray, byte_count_pos: int) -> int:
return data.decode_u32(byte_count_pos) + byte_count_pos + 3
## Returns hex representation of bytestring received
## [param packet]
## [b]return[/b]
func hexlify_packets(packet: PackedByteArray):
if not packet: return ''
var s = String()
for ch in packet:
s += '%02x' % ch
return s