/usr/share/pyshared/pymodbus/utilities.py is in python-pymodbus 0.9.0+r175-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | '''
Modbus Utilities
-----------------
A collection of utilities for packing data, unpacking
data computing checksums, and decode checksums.
'''
import struct
#---------------------------------------------------------------------------#
# Helpers
#---------------------------------------------------------------------------#
def default(value):
'''
Given a python object, return the default value
of that object.
:param value: The value to get the default of
:returns: The default value
'''
return type(value)()
def dict_property(store, index):
''' Helper to create class properties from a dictionary.
Basically this allows you to remove a lot of possible
boilerplate code.
:param store: The store store to pull from
:param index: The index into the store to close over
:returns: An initialized property set
'''
if hasattr(store, '__call__'):
get = lambda self: store(self)[index]
set = lambda self, value: store(self).__setitem__(index, value)
elif isinstance(store, str):
get = lambda self: self.__getattribute__(store)[index]
set = lambda self, value: self.__getattribute__(store).__setitem__(
index, value)
else:
get = lambda self: store[index]
set = lambda self, value: store.__setitem__(index, value)
return property(get, set)
#---------------------------------------------------------------------------#
# Bit packing functions
#---------------------------------------------------------------------------#
def pack_bitstring(bits):
''' Creates a string out of an array of bits
:param bits: A bit array
example::
bits = [False, True, False, True]
result = pack_bitstring(bits)
'''
ret = ''
i = packed = 0
for bit in bits:
if bit: packed += 128
i += 1
if i == 8:
ret += chr(packed)
i = packed = 0
else: packed >>= 1
if i > 0 and i < 8:
packed >>= (7 - i)
ret += chr(packed)
return ret
def unpack_bitstring(string):
''' Creates bit array out of a string
:param string: The modbus data packet to decode
example::
bytes = 'bytes to decode'
result = unpack_bitstring(bytes)
'''
byte_count = len(string)
bits = []
for byte in range(byte_count):
value = ord(string[byte])
for bit in range(8):
bits.append((value & 1) == 1)
value >>= 1
return bits
#---------------------------------------------------------------------------#
# Error Detection Functions
#---------------------------------------------------------------------------#
def __generate_crc16_table():
''' Generates a crc16 lookup table
.. note:: This will only be generated once
'''
result = []
for byte in range(256):
crc = 0x0000
for bit in range(8):
if (byte ^ crc) & 0x0001:
crc = (crc >> 1) ^ 0xa001
else: crc >>= 1
byte >>= 1
result.append(crc)
return result
__crc16_table = __generate_crc16_table()
def computeCRC(data):
''' 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
:returns: The calculated CRC
'''
crc = 0xffff
for a in data:
idx = __crc16_table[(crc ^ ord(a)) & 0xff];
crc = ((crc >> 8) & 0xff) ^ idx
swapped = ((crc << 8) & 0xff00) | ((crc >> 8) & 0x00ff)
return swapped
def checkCRC(data, check):
''' Checks if the data matches the passed in CRC
:param data: The data to create a crc16 of
:param check: The CRC to validate
:returns: True if matched, False otherwise
'''
return computeCRC(data) == check
def computeLRC(data):
''' 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
:returns: The calculated LRC
'''
lrc = 0
lrc = sum(ord(a) for a in data) & 0xff
lrc = (lrc ^ 0xff) + 1
return lrc & 0xff
def checkLRC(data, check):
''' Checks if the passed in data matches the LRC
:param data: The data to calculate
:param check: The LRC to validate
:returns: True if matched, False otherwise
'''
return computeLRC(data) == check
def rtuFrameSize(buffer, byte_count_pos):
''' Calculates the size of the frame based on the byte count.
:param buffer: 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).
'''
return struct.unpack('>B', buffer[byte_count_pos])[0] + byte_count_pos + 3
#---------------------------------------------------------------------------#
# Exported symbols
#---------------------------------------------------------------------------#
__all__ = [
'pack_bitstring', 'unpack_bitstring', 'default',
'computeCRC', 'checkCRC', 'computeLRC', 'checkLRC', 'rtuFrameSize'
]
|