Dans "Essayer l'accès au registre PLC en Python", j'ai essayé l'accès aux mots de base par SLMP en Python. Cette fois, je vais implémenter la manipulation des données lors de la lecture et de l'écriture d'un périphérique bit avec du code binaire en Python.
SLMP propose deux méthodes d'accès continu aux périphériques bit.
Dans le cas d'un accès en unités 1 point, il s'agit d'une représentation de code BCD à 4 bits comme indiqué ci-dessous. Voici un exemple de communication au moment de la lecture.
Pour l'accès par unités de 16 points, 1 bit est une représentation binaire de 1 point. Voici un exemple de communication au moment de la lecture.
Bien entendu, les unités à 16 points sont plus avantageuses pour la lecture et l'écriture de masse en continu.
La commande de communication utilisée est la même que lors de l'accès aux mots, mais comme les données binaires sont compactées comme décrit ci-dessus, la conversion des données est nécessaire pour les traiter comme des données de tableau telles qu'une liste en Python.
C'est assez ennuyeux, et en particulier dans le cas d'unités à 16 points, si vous essayez d'extraire un bit à la fois, vous craignez que cela affecte la vitesse de traitement en Python. Ici, implémentons-le en utilisant l'opération bit de numpy.
Lire avec la commande Lire (0401h
) de la même manière que l'accès par mot. Cependant, la sous-commande est différente de «0001h».
Si le tableau de données reçu est extrait comme [0x00, 0x01, 0x00, 0x11, ...]
, alors [0, 0, 0, 1, 0, 0, 1, 1, ...] Doit être développé comme
.
L'exemple de code ci-dessous développe la séquence de code BDC.
L'argument data
et la valeur de retour sont tous deux le ndarray de numpy (dtype = uint8).
Étant donné que les données de tableau peuvent être calculées dans un lot à l'aide de numpy, le traitement en boucle dans la syntaxe Python n'est pas nécessaire et un traitement à grande vitesse peut être attendu, en particulier lorsque la quantité de données est importante.
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
Le modèle est l'inverse de la lecture. Pack en code BCD. Si les données d'origine sont un nombre impair, les 4 derniers bits sont inutilisés, remplissez-les donc avec 0.
def encode_bcd(data):
"""
Encode 4bit BCD array
[1,2,3,4,...] --> [0x12,0x34,...]
Si le nombre d'entrées est impair, remplissez les 4 derniers bits avec 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
Comme pour l'accès aux mots, la commande Lecture (0401h
) lit et les sous-commandes sont les mêmes.
Puisque le contenu est constitué de données de mots, il s'agit d'une commande de communication.
Comme le montre l'exemple de communication ci-dessus, les données de 4 octets reçues «[34h, 12h, 02h, 00h]» sont
[<M107>, ..., <M100>, <M115>, ..., <M108>, <M123>, ..., <M116>, <M131>, ..., <M124>]
^ ^ ^
Il sera étendu par bits et développé dans un tableau de données de 16 * 4 = 32 points.
La chose déroutante ici est que ** les adresses sont stockées dans l'ordre inverse par octet **.
En d'autres termes, dans l'exemple ci-dessus, le LSB du premier octet "34h" est la valeur de l'adresse de début lorsque la commande de lecture est envoyée, et la valeur des données dont l'adresse est incrémentée vers le MSB.
Et pour le 2ème octet 12h
, cette fois le LSB est la valeur de" start address +8 "...
L'exemple de code ci-dessous utilise unpackbit de numpy pour développer chaque bit de données dans un tableau unidimensionnel. numpy.packbits, [numpy.unpackbits](https://docs.scipy.org/doc/numpy /reference/generated/numpy.unpackbits.html) est une fonction qui convertit un nombre décimal ⇔ un tableau de nombres binaires.
Là où l'ordre de LSB et MSB doit être inversé, le miso est que l'ordre dans la direction de la colonne est inversé en spécifiant la tranche après avoir arrangé une fois en deux dimensions et développé les bits.
def unpack_bits(data):
"""
Développez les chaînes de bits stockées dans l'ordre de LSB dans un tableau
[<M107 ... M100>, <M115 ... M108>] --> [<M100>, ... ,<M107>, <M108>, ... ,<M115>]
"""
#Afin d'inverser l'ordre des données après décompression des bits, sous la forme d'un pseudo tableau bidimensionnel,
#Assurez-vous que les données de bit sont stockées pour chaque octet
# ex. [1,2,3] --> [[1],[2],[3]]
byteArray2D = data.reshape((data.size, 1))
#Développer les données de bits
# 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)
#Après avoir inversé l'ordre dans le sens de la colonne, revenez au tableau unidimensionnel
return byteArray2D_bin[:, ::-1].flatten()
Le modèle est l'inverse de la lecture. Packs 0/1 données de tableau à 1 dimension dans une chaîne d'octets
def pack_bits(data):
"""
Pack d'un tableau de données binaires dans une chaîne d'octets stockée dans l'ordre à partir de LSB
[<M100>, ... ,<M107>, <M108>, ... ,<M115>] --> [<M107 ... M100>, <M115 ... M108>]
"""
#Assurez-vous que le nombre de données est un multiple de 8
size8 = -(-data.size // 8) * 8
#Remplissez 0 lorsque la dernière taille est insuffisante
byteArray_bin = np.zeros(size8, 'u1')
byteArray_bin[:size8] = data
#Converti en un tableau bidimensionnel pour inverser l'ordre des données tous les 8 bits
byteArray2D_bin = byteArray_bin.reshape((size8//8, 8))
#Pack de données de bits
return np.packbits(byteArray2D_bin[:, ::-1])
Recommended Posts