initial
This commit is contained in:
319
env/lib/python3.11/site-packages/pyModbusTCP/utils.py
vendored
Normal file
319
env/lib/python3.11/site-packages/pyModbusTCP/utils.py
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
""" pyModbusTCP utils functions """
|
||||
|
||||
import re
|
||||
import socket
|
||||
import struct
|
||||
|
||||
|
||||
###############
|
||||
# bits function
|
||||
###############
|
||||
def get_bits_from_int(val_int, val_size=16):
|
||||
"""Get the list of bits of val_int integer (default size is 16 bits).
|
||||
|
||||
Return bits list, the least significant bit first. Use list.reverse() for msb first.
|
||||
|
||||
:param val_int: integer value
|
||||
:type val_int: int
|
||||
:param val_size: bit length of integer (word = 16, long = 32) (optional)
|
||||
:type val_size: int
|
||||
:returns: list of boolean "bits" (the least significant first)
|
||||
:rtype: list
|
||||
"""
|
||||
bits = []
|
||||
# populate bits list with bool items of val_int
|
||||
for i in range(val_size):
|
||||
bits.append(bool((val_int >> i) & 0x01))
|
||||
# return bits list
|
||||
return bits
|
||||
|
||||
|
||||
# short alias
|
||||
int2bits = get_bits_from_int
|
||||
|
||||
|
||||
def byte_length(bit_length):
|
||||
"""Return the number of bytes needs to contain a bit_length structure.
|
||||
|
||||
:param bit_length: the number of bits
|
||||
:type bit_length: int
|
||||
:returns: the number of bytes
|
||||
:rtype: int
|
||||
"""
|
||||
return (bit_length + 7) // 8
|
||||
|
||||
|
||||
def test_bit(value, offset):
|
||||
"""Test a bit at offset position.
|
||||
|
||||
:param value: value of integer to test
|
||||
:type value: int
|
||||
:param offset: bit offset (0 is lsb)
|
||||
:type offset: int
|
||||
:returns: value of bit at offset position
|
||||
:rtype: bool
|
||||
"""
|
||||
mask = 1 << offset
|
||||
return bool(value & mask)
|
||||
|
||||
|
||||
def set_bit(value, offset):
|
||||
"""Set a bit at offset position.
|
||||
|
||||
:param value: value of integer where set the bit
|
||||
:type value: int
|
||||
:param offset: bit offset (0 is lsb)
|
||||
:type offset: int
|
||||
:returns: value of integer with bit set
|
||||
:rtype: int
|
||||
"""
|
||||
mask = 1 << offset
|
||||
return int(value | mask)
|
||||
|
||||
|
||||
def reset_bit(value, offset):
|
||||
"""Reset a bit at offset position.
|
||||
|
||||
:param value: value of integer where reset the bit
|
||||
:type value: int
|
||||
:param offset: bit offset (0 is lsb)
|
||||
:type offset: int
|
||||
:returns: value of integer with bit reset
|
||||
:rtype: int
|
||||
"""
|
||||
mask = ~(1 << offset)
|
||||
return int(value & mask)
|
||||
|
||||
|
||||
def toggle_bit(value, offset):
|
||||
"""Return an integer with the bit at offset position inverted.
|
||||
|
||||
:param value: value of integer where invert the bit
|
||||
:type value: int
|
||||
:param offset: bit offset (0 is lsb)
|
||||
:type offset: int
|
||||
:returns: value of integer with bit inverted
|
||||
:rtype: int
|
||||
"""
|
||||
mask = 1 << offset
|
||||
return int(value ^ mask)
|
||||
|
||||
|
||||
########################
|
||||
# Word convert functions
|
||||
########################
|
||||
def word_list_to_long(val_list, big_endian=True, long_long=False):
|
||||
"""Word list (16 bits) to long (32 bits) or long long (64 bits) list.
|
||||
|
||||
By default, word_list_to_long() use big endian order. For use little endian, set
|
||||
big_endian param to False. Output format could be long long with long_long.
|
||||
option set to True.
|
||||
|
||||
:param val_list: list of 16 bits int value
|
||||
:type val_list: list
|
||||
:param big_endian: True for big endian/False for little (optional)
|
||||
:type big_endian: bool
|
||||
:param long_long: True for long long 64 bits, default is long 32 bits (optional)
|
||||
:type long_long: bool
|
||||
:returns: list of 32 bits int value
|
||||
:rtype: list
|
||||
"""
|
||||
long_list = []
|
||||
block_size = 4 if long_long else 2
|
||||
# populate long_list (len is half or quarter of 16 bits val_list) with 32 or 64 bits value
|
||||
for index in range(int(len(val_list) / block_size)):
|
||||
start = block_size * index
|
||||
long = 0
|
||||
if big_endian:
|
||||
if long_long:
|
||||
long += (val_list[start] << 48) + (val_list[start + 1] << 32)
|
||||
long += (val_list[start + 2] << 16) + (val_list[start + 3])
|
||||
else:
|
||||
long += (val_list[start] << 16) + val_list[start + 1]
|
||||
else:
|
||||
if long_long:
|
||||
long += (val_list[start + 3] << 48) + (val_list[start + 2] << 32)
|
||||
long += (val_list[start + 1] << 16) + val_list[start]
|
||||
long_list.append(long)
|
||||
# return long list
|
||||
return long_list
|
||||
|
||||
|
||||
# short alias
|
||||
words2longs = word_list_to_long
|
||||
|
||||
|
||||
def long_list_to_word(val_list, big_endian=True, long_long=False):
|
||||
"""Long (32 bits) or long long (64 bits) list to word (16 bits) list.
|
||||
|
||||
By default long_list_to_word() use big endian order. For use little endian, set
|
||||
big_endian param to False. Input format could be long long with long_long
|
||||
param to True.
|
||||
|
||||
:param val_list: list of 32 bits int value
|
||||
:type val_list: list
|
||||
:param big_endian: True for big endian/False for little (optional)
|
||||
:type big_endian: bool
|
||||
:param long_long: True for long long 64 bits, default is long 32 bits (optional)
|
||||
:type long_long: bool
|
||||
:returns: list of 16 bits int value
|
||||
:rtype: list
|
||||
"""
|
||||
word_list = []
|
||||
# populate 16 bits word_list with 32 or 64 bits value of val_list
|
||||
for val in val_list:
|
||||
block_l = [val & 0xffff, (val >> 16) & 0xffff]
|
||||
if long_long:
|
||||
block_l.append((val >> 32) & 0xffff)
|
||||
block_l.append((val >> 48) & 0xffff)
|
||||
if big_endian:
|
||||
block_l.reverse()
|
||||
word_list.extend(block_l)
|
||||
# return long list
|
||||
return word_list
|
||||
|
||||
|
||||
# short alias
|
||||
longs2words = long_list_to_word
|
||||
|
||||
|
||||
##########################
|
||||
# 2's complement functions
|
||||
##########################
|
||||
def get_2comp(val_int, val_size=16):
|
||||
"""Get the 2's complement of Python int val_int.
|
||||
|
||||
:param val_int: int value to apply 2's complement
|
||||
:type val_int: int
|
||||
:param val_size: bit size of int value (word = 16, long = 32) (optional)
|
||||
:type val_size: int
|
||||
:returns: 2's complement result
|
||||
:rtype: int
|
||||
:raises ValueError: if mismatch between val_int and val_size
|
||||
"""
|
||||
# avoid overflow
|
||||
if not (-1 << val_size - 1) <= val_int < (1 << val_size):
|
||||
err_msg = 'could not compute two\'s complement for %i on %i bits'
|
||||
err_msg %= (val_int, val_size)
|
||||
raise ValueError(err_msg)
|
||||
# test negative int
|
||||
if val_int < 0:
|
||||
val_int += 1 << val_size
|
||||
# test MSB (do two's comp if set)
|
||||
elif val_int & (1 << (val_size - 1)):
|
||||
val_int -= 1 << val_size
|
||||
return val_int
|
||||
|
||||
|
||||
# short alias
|
||||
twos_c = get_2comp
|
||||
|
||||
|
||||
def get_list_2comp(val_list, val_size=16):
|
||||
"""Get the 2's complement of Python list val_list.
|
||||
|
||||
:param val_list: list of int value to apply 2's complement
|
||||
:type val_list: list
|
||||
:param val_size: bit size of int value (word = 16, long = 32) (optional)
|
||||
:type val_size: int
|
||||
:returns: 2's complement result
|
||||
:rtype: list
|
||||
"""
|
||||
return [get_2comp(val, val_size) for val in val_list]
|
||||
|
||||
|
||||
# short alias
|
||||
twos_c_l = get_list_2comp
|
||||
|
||||
|
||||
###############################
|
||||
# IEEE floating-point functions
|
||||
###############################
|
||||
def decode_ieee(val_int, double=False):
|
||||
"""Decode Python int (32 bits integer) as an IEEE single or double precision format.
|
||||
|
||||
Support NaN.
|
||||
|
||||
:param val_int: a 32 or 64 bits integer as an int Python value
|
||||
:type val_int: int
|
||||
:param double: set to decode as a 64 bits double precision,
|
||||
default is 32 bits single (optional)
|
||||
:type double: bool
|
||||
:returns: float result
|
||||
:rtype: float
|
||||
"""
|
||||
if double:
|
||||
return struct.unpack("d", struct.pack("Q", val_int))[0]
|
||||
else:
|
||||
return struct.unpack("f", struct.pack("I", val_int))[0]
|
||||
|
||||
|
||||
def encode_ieee(val_float, double=False):
|
||||
"""Encode Python float to int (32 bits integer) as an IEEE single or double precision format.
|
||||
|
||||
Support NaN.
|
||||
|
||||
:param val_float: float value to convert
|
||||
:type val_float: float
|
||||
:param double: set to encode as a 64 bits double precision,
|
||||
default is 32 bits single (optional)
|
||||
:type double: bool
|
||||
:returns: IEEE 32 bits (single precision) as Python int
|
||||
:rtype: int
|
||||
"""
|
||||
if double:
|
||||
return struct.unpack("Q", struct.pack("d", val_float))[0]
|
||||
else:
|
||||
return struct.unpack("I", struct.pack("f", val_float))[0]
|
||||
|
||||
|
||||
################
|
||||
# misc functions
|
||||
################
|
||||
def crc16(frame):
|
||||
"""Compute CRC16.
|
||||
|
||||
:param frame: frame
|
||||
:type frame: bytes
|
||||
:returns: CRC16
|
||||
:rtype: int
|
||||
"""
|
||||
crc = 0xFFFF
|
||||
for item in frame:
|
||||
next_byte = item
|
||||
crc ^= next_byte
|
||||
for _ in range(8):
|
||||
lsb = crc & 1
|
||||
crc >>= 1
|
||||
if lsb:
|
||||
crc ^= 0xA001
|
||||
return crc
|
||||
|
||||
|
||||
def valid_host(host_str):
|
||||
"""Validate a host string.
|
||||
|
||||
Can be an IPv4/6 address or a valid hostname.
|
||||
|
||||
:param host_str: the host string to test
|
||||
:type host_str: str
|
||||
:returns: True if host_str is valid
|
||||
:rtype: bool
|
||||
"""
|
||||
# IPv4 valid address ?
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET, host_str)
|
||||
return True
|
||||
except socket.error:
|
||||
pass
|
||||
# IPv6 valid address ?
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET6, host_str)
|
||||
return True
|
||||
except socket.error:
|
||||
pass
|
||||
# valid hostname ?
|
||||
if re.match(r'^[a-z][a-z0-9.\-]+$', host_str):
|
||||
return True
|
||||
# on invalid host
|
||||
return False
|
||||
Reference in New Issue
Block a user