在python中解析缓冲区字符串的最佳方法

时间:2018-05-02 07:37:47

标签: python parsing pyserial uart

我正在研究一种通过Uart发送命令的嵌入式系统。 Uart工作在115200波特

在PC端,我想阅读这些命令,解析它们并执行相关操作。

我选择python作为构建脚本的语言。

这是从嵌入式系统收到的典型命令:

S;SEND;40;{"ID":"asg01","T":1,"P":{"T":180}};E

每条消息以S开头,以E结尾。 与消息关联的命令是“SEND”,有效负载长度为40.

我的想法是读取来自UART的字节,并且:

  • 检查消息是否以S
  • 开头
  • 检查消息是否以E
  • 结尾
  • 如果上述假设为真,则拆分消息以查找命令和有效负载。

哪种解析来自异步uart的所有字节的最佳方法是什么?

我担心由于错误(或缓慢)解析导致的消息丢失。

感谢您的帮助!

BR, 费德里科

2 个答案:

答案 0 :(得分:1)

在我的日常工作中,我使用UART协议以115,200波特编写了一个嵌入式系统软件和一台通过USB电缆相互通信的PC。

我看到您使用PySerial标记了帖子,因此您已经了解了Python最受欢迎的串行端口通信包。我将补充一点,如果您使用的是PyQt,那么该软件包中也包含一个串行模块。

115,200波特对现代台式电脑来说并不快。我怀疑你在PC端进行的任何解析都无法跟上。我使用PyQt实时解析数据流和绘制我的数据图。

我在通过UART在嵌入式系统和PC之间进行通信的工作中注意到,有些数据偶尔会被破坏。字节可能会出现乱码,重复或丢弃。此外,即使没有添加或删除字节,您也可以偶尔执行读取,同时只有部分数据包在缓冲区中,并且读取将提前终止。如果您使用40字节的固定读取长度并且相信每次读取将始终与上面显示的数据包完全对齐,那么您将经常出错。

为了解决这些问题,我在Python中编写了一个FIFO类,它使用FIFO头部的串行端口数据,在尾部产生有效的数据包,并丢弃无效数据。我的FIFO保存的数量是我的数据包的3倍,所以如果我使用特定的序列查找数据包边界,我有很多路标。

还有一些建议:如果你有选择的话,可以使用Python 3,它更干净。使用bytes和bytearray对象。不要使用str,因为你会发现自己在Unicode和ASCII之间来回转换。

答案 1 :(得分:0)

此格式几乎可解析为csv,但不完全,因为第四个字段是JSON,您可能无法保证JSON不会包含嵌入分号的任何字符串。所以,我想你可能只想使用字符串(或者更确切地说是字节)操作函数:

def parsemsg(buf):
    s, cmd, length, rest = buf.split(b';', 3)
    j, _, e = rest.rpartition(b';')
    if s != b'S' or e != b'E':
        raise ValueError('must start with S and end with E')
    return cmd.decode('utf-8'), int(length), json.loads(j)

然后:

>>> parsemsg(b'S,SEND,40,{"ID":"asg01","T":1,"P":{"T":180}},E')
('SEND', 40, {'ID': 'asg01', 'T': 1, 'P': {'T': 180}})

实际的分号解析部分在我的笔记本电脑上占用602ns,decodeint将其提升到902ns。另一方面,json.loads需要10us。所以,如果你担心性能,那么JSON部分确实是唯一重要的部分(尝试我碰巧安装的第三方JSON库,最快的那个仍然是8.1us,这不是很多更好)。你也可以保持其他一切简单而强大。

另外,考虑到您以115000波特率读取此信息,您无法以大约6毫秒的速度获取这些消息,因此首先花费11us解析它们甚至不会出现问题。