In "Try PLC register access in Python", I tried basic word access by SLMP in Python. This time, I will implement the data manipulation when reading and writing a bit device with binary code in Python.
SLMP offers two continuous access methods to bit devices.
--Read and write values from bit devices (consecutive device numbers) in 1-point units --Read and write values in 16-point units from bit devices (consecutive device numbers)
In the case of access in 1-point units, it is a 4-bit BCD code representation as shown below. The following is an example of communication at the time of reading.
For access in units of 16 points, 1 bit is a binary representation of 1 point. The following is an example of communication at the time of reading.
Of course, 16-point units are more advantageous for continuous mass reading and writing.
The communication command used is the same as when accessing words, but since bit data is packed as described above, data conversion is required to handle it as array data such as a list in Python.
This is quite annoying, and especially in the case of 16-point units, if you try to extract bit by bit normally, there is a concern that it will affect the processing speed in Python. Here, I will implement it using numpy bit operations.
Read with the Read command (0401h
) in the same way as word access. However, the subcommand is different from 0001h
.
If the received data array is extracted as [0x00, 0x01, 0x00, 0x11, ...]
, then [0, 0, 0, 1, 0, 0, 1, 1, ...] Must be expanded like
.
The sample code below expands the BDC code array.
The argument data
and the return value are both numpy ndarray (dtype = uint8).
Since array data can be calculated in a batch by using numpy, loop processing in Python syntax is not required, and high-speed processing can be expected especially when the amount of data is large.
import numpy as np
# ex. data = np.array([0x12, 0x34, 0x56], 'u1')
def decode_bcd(data):
"""
Decode 4bit BCD array
[0x12,0x34,...] --> [1,2,3,4,...]
"""
binArrayH = (data >> 4) & 0x0F
binArrayL = data & 0x0F
binArray = np.empty(data.size * 2, 'u1')
binArray[::2] = binArrayH
binArray[1::2] = binArrayL
return binArray
The pattern is the reverse of reading. Pack in BCD code. If the original data is an odd number, the last 4 bits are unused, so fill them with 0.
def encode_bcd(data):
"""
Encode 4bit BCD array
[1,2,3,4,...] --> [0x12,0x34,...]
If the number of inputs is odd, fill the last 4 bits with 0
"""
binArrayH = (data[::2] & 0x0F) << 4
binArrayL = data[1::2] & 0x0F
binArray = np.zeros_like(binArrayH)
if data.size % 2 == 0:
binArray = binArrayH | binArrayL
else:
binArray[:-1] = binArrayH[:-1] | binArrayL
binArray[-1] = binArrayH[-1]
return binArray
As with word access, the Read command (0401h
) reads and the subcommands are the same.
Since the content is word data, it is the same as a communication command.
As shown in the communication example above, the received 4-byte data [34h, 12h, 02h, 00h]
is
[<M107>, ..., <M100>, <M115>, ..., <M108>, <M123>, ..., <M116>, <M131>, ..., <M124>]
^ ^ ^
It will be expanded into a data array of 16 * 4 = 32 points by bit expansion as in.
The confusing thing here is that ** the addresses are stored in reverse order per byte **.
In other words, in the above example, the LSB of the first 1 byte 34h
is the value of the start address when the read command is sent, and the value of the data whose address is incremented toward the MSB.
And for the 2nd byte 12h
, the LSB is the value of" start address +8 "...
The sample code below uses numpy's unpackbit to expand each bit data into a one-dimensional array. numpy.packbits, [numpy.unpackbits](https://docs.scipy.org/doc/numpy /reference/generated/numpy.unpackbits.html) is a function that converts a decimal number ⇔ binary number array.
Where the order of LSB and MSB must be reversed, the miso is that the order in the column direction is reversed by specifying the slice after once arranging it in two dimensions and expanding the bits.
def unpack_bits(data):
"""
Expands the bit strings stored in order from the LSB into an array
[<M107 ... M100>, <M115 ... M108>] --> [<M100>, ... ,<M107>, <M108>, ... ,<M115>]
"""
#In order to reverse the data order after unpackbits, as a pseudo two-dimensional array,
#Bit data should be stored for each byte
# ex. [1,2,3] --> [[1],[2],[3]]
byteArray2D = data.reshape((data.size, 1))
#Expand bit data
# ex. [[1],[2],[3]] --> [[0,0,0,0,0,0,0,1],[0,0,0,0,0,0,1,0],[0,0,0,0,0,0,1,1]]
byteArray2D_bin = np.unpackbits(byteArray2D, axis=1)
#After reversing the order in the column direction, return to the one-dimensional array
return byteArray2D_bin[:, ::-1].flatten()
The pattern is the reverse of reading. Packs 0/1 one-dimensional array data into a byte string.
def pack_bits(data):
"""
Pack an array of bit data into a byte string stored in order from the LSB
[<M100>, ... ,<M107>, <M108>, ... ,<M115>] --> [<M107 ... M100>, <M115 ... M108>]
"""
#Make sure the number of data is a multiple of 8
size8 = -(-data.size // 8) * 8
#Fill in 0 where the last size is insufficient
byteArray_bin = np.zeros(size8, 'u1')
byteArray_bin[:size8] = data
#Converted to a two-dimensional array to invert the data order every 8 bits
byteArray2D_bin = byteArray_bin.reshape((size8//8, 8))
#Pack bit data
return np.packbits(byteArray2D_bin[:, ::-1])
--Mitsubishi Electric SLMP Reference Manual
Recommended Posts