【问题标题】:How to write a Python async serial async reader?如何编写 Python 异步串行异步阅读器?
【发布时间】:2021-02-06 01:04:10
【问题描述】:

我正在将一些 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.x python-asyncio


    【解决方案1】:

    但是我不知道如何将 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 相比没有任何优势。

    【讨论】:

    • 谢谢。我仍然对我之前的异步体验感到困惑。在以前的所有情况下,我都能够请求读取与缓冲区中等待的字节数相匹配的长度。似乎对于 Python,对于完全异步(一般意义上,不确定何时或谁将首先发送字节)通信,我只需要在循环中读取(1)?我被tinkering.xyz/async-serial 弄糊涂了,这让我看起来不得不做一些循环恶作剧。但是,在你的帮助下,我能够让它工作!这比看起来要简单得多!
    • @UserOneFourTwo 我没有亲自使用过异步串行,但是对于一般的异步,您绝对不需要需要在循环中调用read(1)。像await reader.read(1024) 这样的东西会给你尽可能多的数据,1024 是上限。但是如果只有 3 个字节到达,你应该立即得到它们。
    • 谢谢。由于您的回答,在使其工作后进行试验后,似乎只是普通的旧 read() 就可以了。
    猜你喜欢
    • 2013-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    • 2021-12-19
    • 2015-09-19
    • 1970-01-01
    相关资源
    最近更新 更多