#python-3.x #python-asyncio
#python-3.x #python-asyncio
Вопрос:
Я переношу некоторый код 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(amp;QAbstractSocket::readyRead, amp;Arduino::handleBytes);
В узле:
arduino.on('data', line => console.log(line))
В Python, похоже, нет никакого очевидного ответа? Как мне получить байты последовательного порта, которые поступают для передачи в Arduino.receive (self, data)?
Ответ №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.
Комментарии:
1. Спасибо за это. Я все еще смущен своим предыдущим опытом асинхронной работы. Во всех предыдущих случаях я мог запрашивать чтение длины, соответствующей количеству байтов, ожидающих в буфере. Кажется, что с Python для полностью асинхронной (в общем смысле, не уверен, когда или кто отправит байты первым) связи мне просто нужно прочитать (1) в цикле? Я был сбит с толку tinkering.xyz/async-serial , из-за чего казалось, что мне пришлось проделать несколько циклов. Но я смог заставить его работать с вашей помощью! Это намного проще, чем кажется!
2. @UserOneFourTwo Я лично не использовал async-serial, но с общим asyncio вам определенно не нужно вызывать
read(1)
цикл. Что-то вродеawait reader.read(1024)
даст вам как можно больше данных, причем 1024 является верхним пределом. Но если поступает только 3 байта, вы должны получить их немедленно.3. Спасибо. После экспериментов после того, как он заработал благодаря вашему ответу, кажется, что просто старый read() подходит.