首页 > 解决方案 > 如何编写 Python 异步串行异步阅读器?

问题描述

我正在将一些 C++ 代码移植到 Python,并且我花了很长时间弄清楚如何为串行字节提供 onReceive 处理程序。我在用

import serial_asyncio
class Arduino: #comms is with an Arduino in serial mode)
    async def connect(self, serial_port):
        (self.serial_reader, self.serial_writer) = await 
        serial_asyncio.open_serial_connection(url=serial_port, baudrate=115200)
        print("serial opened:", serial_port)
        self.buffer = b""

    async def recv(self, data):
        self.buffer += data
        self.process_buffer(self.buffer)

if __name__ == '__main__':
     ardunio = Arduino("/dev/ttyS0")         
     loop = asyncio.get_event_loop()
     loop.run_until_complete(ardunio.connect())
     loop.run_forever()

但是我无法弄清楚如何将 recv 处理程序修补到读取中。我Qt,我可以:

connect(&QAbstractSocket::readyRead, &Arduino::handleBytes);

在节点中:

arduino.on('data', line => console.log(line))

在 Python 中,似乎没有任何明显的答案?如何将到达的串行端口字节传递给 Arduino.receive(self, data)?

标签: python-3.xpython-asyncio

解决方案


但是我无法弄清楚如何将 recv 处理程序修补到读取中。

open_serial_connection不是基于回调的接口,它返回一对通过协程公开内容的流。这使您可以像编写阻塞代码一样与串行端口通信,即无需使用回调和缓冲区来构建数据。例如(未经测试):

async def main():
    reader, writer = await serial_asyncio.connect(url="/dev/ttyS0", baudrate=115200)
    # instead of: arduino.on('data', line => console.log(line))
    # ...we can just read some data from the serial port
    data = await reader.read(1024)
    # ...and print it right here
    print(repr(data))

asyncio.run(main())

像这样StreamReader.read的协程看起来会阻塞等待数据,但实际上它们只是暂停当前协程并让事件循环做其他事情。这使您可以在等待数据从串行端口到达时轻松表达超时或进行其他处理。

如果您仍然需要回调,例如因为您需要与 C API 通信,您有两种选择:

  • 使用底层create_serial_connection函数。它接受一个继承类型,asyncio.Protocol您可以在其中定义钩子data_received(作为回调,而不是协程),这与您对Arduino类建模的方式很接近。

  • 继续使用协程 API,但用于add_done_callback注册回调以在协程准备好时运行。

后者的一个例子是:

async def main():
    reader, writer = await serial_asyncio.connect(url="/dev/ttyS0", baudrate=115200)
    # Python equivalent of arduino.on('data', x):
    # 1. call reader.read() but don't await - instead, create a "future"
    # which works like a JavaScript Promise
    future = asyncio.ensure_future(reader.read(1024))
    # register a done callback when the result is available
    future.add_done_callback(future, lambda _: print(repr(future.result())))
    # go do something else - here we wait for an event just so main()
    # doesn't exit immediately and terminate our program
    await asyncio.Event().wait()

asyncio.run(main())

但是除非你正在与 C 通信,否则我认为使用这种风格比普通的 async/await 没有优势。


推荐阅读