将字节流反序列化为对象

时间:2016-01-31 13:50:31

标签: python object serialization stream byte

我正在构建一个从串口接收字节的python项目。字节是对发送的命令的响应(也通过串行端口)。响应没有识别标记,即仅从字节开始,我不知道这是哪个命令响应。解码器当然需要事先知道哪个命令是对它的响应。

我希望将传入的字节序列表示为嵌套对象,指示帧,标头,有效负载,解码的有效负载等。我更倾向于一次将1个字节推送到解码器并让它调用一旦收到足够的字节用于完整对象(或者如果有错误或超时则返回errorCallback)回调。

实际帧具有起始字节和结束字节。它有一个带有几个字节的头(一些id,命令状态(基本上是ok / fail)),一个是数据长度字节。接下来是数据,后跟校验和(单字节)。数据是对命令的响应。

响应是可预测的,因为前面的字节决定了即将到来的字节是什么意思。

回复示例:

AA:00:0C:00:01:00:00:D3:8D:D4:5C:50:01:04:E0:6E:BB

细分:

aa: start frame
    00: id
    0c: data length (incl status): 12 bytes
    00: command status (byte 1)
        01: 1 data frame (byte 2)
            00:00: flags of first data frame (byte 3-4)
            d3:8d:d4:5c:50:01:04:e0: first data (aa and bb could be in it) (byte 5-12)
    6e: checksum (includes all bytes except start, checksum, end bytes)
bb: end frame

这是串口通信,字节可能丢失(并且额外产生),我希望使用超时来处理重置(没有先发送命令就不会有响应)。

我真的想要一种面向对象的方法,其中解码器会产生一个对象,当序列化时,它会再次产生相同的字节序列。我使用的是python 2.7,但实际上任何面向对象的语言都可以(只要我可以将它转换为python)。

我只是不确定如何构造解码器以使其看起来整洁。我正在寻找一个完整的解决方案,只是让我朝着正确的方向前进(正确的方向在这里有点主观)。

1 个答案:

答案 0 :(得分:1)

我不完全明白你想要做什么,但如果你想从某些设备接收固定长度的响应并使它们成为某个对象的属性,那么这样的事情会好吗?

START_FRAME = 0xAA
END_FRAME = 0xBB
TIMEOUT = 2

class Response:
def __init__(self, response):
    if (len(response) - 6) % 11 == 0 and response[0] == START_FRAME and response[-1] == END_FRAME:  # verify that its a valid response
        self.header = {}   # build header
        self.header['id'] = response[1]
        self.header['length'] = response[2]
        self.header['status'] = response[3]
        self.r_checksum = response[-2]  # get checksum from response
        self.checksum = self.header['id'] ^ self.header['length'] ^ self.header['status']   # verify the checksum
        self.payload = response[4:-2] # get raw payload slice
        self.decode(self.payload)  # parse payload
        if self.r_checksum == self.checksum: # final check
            self.parsed = True
        else:
            self.parsed = False
    else:  # if response didnt follow the pattern
        self.parsed = False
def decode(self, packet):  # decode payload
    self.frm_count = packet[0]   # get number of data frames
    self.checksum ^= self.frm_count  
    self.decoded = []   # hold decoded payload
    frames = packet[1:]
    for c in range(self.frm_count):  # iterate through data frames
        flags = frames[(c*10):(c*10 + 2)]
        for f in flags:
            self.checksum ^= f
        data = frames[(c*10 + 2):(c+1)*10]
        for d in data:
            self.checksum ^= d
        self.decoded.append({'frame': c+1, 'flags': flags, 'data':data})
def serialize():  # reconstruct response
    res = bytearray()
    res.append(START_FRAME)
    res.extend([self.header['id'], self.header['length'], self.header['status']])
    res.extend(self.payload)
    res.extend([self.checksum, END_FRAME])
    return res

response = bytearray()
ser = serial.Serial('COM3', 9600)   # timeout is 2 seconds
last_read = time.clock()
while time.clock() -  last_read < TIMEOUT:
    while ser.inWaiting() > 0:
        response.append(ser.read())
        last_read = time.clock()
decoded_res = Response(response)
if decoded_res.parsed:
    # do stuff
else:
    print('Invalid response!')

此代码假定可能有多个数据帧,数据帧前面紧跟一个表示数据帧数的字节。 与串行通信所用的时间(即使是115200波特)相比,解析数据包的速度很快。我认为整个事情大致是O(n)。