4

I have a class with all sorts of data in it, like:

class UARTMessage:
    Identification1 = int(0) #byte 0
    Timestamp1 = int(0) #bytes [1:5]
    Voltage1 = int(0) #bytes [6:7]
    Current1 = int(0) #bytes [8:9]
    Signal1= int(0) #bytes [10:11]
    Identification2 = int(0) #byte 12
    Timestamp2 = int(0) #bytes [13:17]
    Voltage2 = int(0) #bytes [18:19]
    Current2 = int(0) #bytes [20:21]
    Signal = int(0) #bytes [22:23]
    Identification3 = int(0) #byte 24   

The data to fill this structure up will come from a serial. I need to deserialize the data coming from the serial in the shape of this structure. I am reading from serial 40 bytes data chunks and I need to split itit. I tried pickle library but it seems that it's not fitted exactly for deserializing this type of data. I found struct but I cannot understand how to use it proprely in this case.
As the comments in the struct, I need to desearilize the chunks of data like: first byte is Identificator, bytes from 1 to 5 included is the timestamp and so on....
DO you have any ideea how can I achieve this?
Thanks

cwallenpoole
  • 79,954
  • 26
  • 128
  • 166
Lucian
  • 874
  • 11
  • 33
  • Are you sure that timestamp is 5 bytes long? It's easy to do with `struct` module, but 5 bytes doesn't correspond to any C data type. – warownia1 Aug 08 '16 at 15:49

1 Answers1

7

First of all, we need to declare format of incoming bytes according to this list: https://docs.python.org/3/library/struct.html?highlight=struct#format-characters.

import struct
import sys


class UARTMessage:

    fmt = '@B5shhhB5shhhB'

    def __init__(self, data_bytes):
        fields = struct.unpack(self.fmt, data_bytes)
        (self.Identification1,
         self.Timestamp1,
         self.Voltage1,
         self.Current1,
         self.Signal1,
         self.Identification2,
         self.Timestamp2,
         self.Voltage2,
         self.Current2,
         self.Signal2,
         self.Identification3) = fields
        self.Timestamp1 = int.from_bytes(self.Timestamp1, sys.byteorder)
        self.Timestamp2 = int.from_bytes(self.Timestamp2, sys.byteorder)
        self.Timestamp3 = int.from_bytes(self.Timestamp3, sys.byteorder)

First character of the fmt is byte order. @ is python default (usually little endian), if you need to use network big-endian put !. Each subsequent character represents a data type which comes from the bytes stream.

Next, in the initializer, I unpack bytes according to the recipe in fmt into a fields tuple. Next, I assign the values of the tuple to object attributes. Timestamp has unusual length of 5 bytes, so it requires special treatment. It is fetched as 5-bytes string (5s in fmt) and converted to int using int.from_bytes function with system default bytes order (if you need a different bytes order enter 'big' or 'little' as a second argument).

When you want to create your structure, pass the sequence of bytes to the constructor.

warownia1
  • 2,771
  • 1
  • 22
  • 30
  • As described in https://docs.python.org/3/library/stdtypes.html int.from_bytes is only available in python 3.2. I am working with 2.7. – Lucian Aug 09 '16 at 10:40
  • python 2 alternative to `from_bytes` is `int(codecs.encode(b'\x00\x01', 'hex'), 16)` (it's equivalent to little-endian order) http://stackoverflow.com/questions/30402743/python-2-7-equivalent-of-built-in-method-int-from-bytes – warownia1 Aug 09 '16 at 10:51