Escape-последовательности прерывания Pyserial клиента
Я пишу клиент для сетевого устройства с помощью pyserial
которая зависает на пару секунд после входа в устройство и до получения приглашения на устройстве. Отслеживание байтов по строке Я вижу, что следующие задерживающие последовательности являются причиной задержки:
0003a0 73 65 20 6c 65 76 65 6c 0d 0a 0d 1b 5b 39 39 39 |se level....[999|
0003b0 39 42 0d 1b 5b 39 39 39 39 42 1b 5a 20 20 1b 5b |9B..[9999B.Z .[|
0003c0 36 6e 0d 0d 0d 0d 5b 61 64 6d 69 6e 40 73 77 69 |6n....[admin@swi|
0003d0 74 63 68 5d 20 3e 20 20 20 20 20 20 20 20 20 20 |tch] > |
Когда используешь screen
для подключения к устройству, задержка не отображается как screen
отвечает соответствующим образом.
0003a0 73 65 20 6c 65 76 65 6c 0d 0a 0d 1b 5b 39 39 39 |se level....[999|
0003b0 39 42 0d 1b 5b 39 39 39 39 42 1b 5a 20 |9B..[9999B.Z |
---=== WRITE ===---
000000 1b 5b 3f 31 3b 32 63 |.[?1;2c |
---=== READ ===---
000000 20 1b 5b 36 6e | .[6n |
---=== WRITE ===---
000000 1b 5b 35 37 3b 33 52 |.[57;3R |
---=== READ ===---
000000 1b 5b 34 6c 1b 5b 32 30 6c 1b 5b 3f 34 37 6c 1b |.[4l.[20l.[?47l.|
000010 5b 3f 37 68 1b 5b 3f 35 6c 1b 5b 3f 32 35 68 1b |[?7h.[?5l.[?25h.|
000020 5b 48 1b 5b 39 39 39 39 42 1b 5b 36 6e |[H.[9999B.[6n |
---=== WRITE ===---
000000 1b 5b 35 37 3b 31 52 |.[57;1R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 42 1b 44 1b 5b 39 39 |.[H.[9999B.D.[99|
000010 39 39 41 1b 5b 36 6e |99A.[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 52 |.[1;1R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 43 1b 5b 36 6e |.[H.[9999C.[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 31 52 |.[1;141R |
---=== READ ===---
000000 1b 5b 48 c4 9b 48 1b 5b 36 6e 0d 20 20 20 |.[H..H.[6n. |
---=== WRITE ===---
000000 1b 5b 31 3b 33 52 |.[1;3R |
---=== READ ===---
000000 1b 5b 48 1b 5b 39 39 39 39 43 1b 5b 36 6e 20 1b |.[H.[9999C.[6n .|
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 31 52 |.[1;141R |
---=== READ ===---
000000 5b 36 6e 20 1b 5b 36 6e |[6n .[6n |
---=== WRITE ===---
000000 1b 5b 31 3b 31 34 32 52 1b 5b 32 3b 32 52 |.[1;142R.[2;2R |
---=== READ ===---
000000 1b 5b 33 3b 35 72 1b 5b 48 1b 5b 36 6e 0a 0a |.[3;5r.[H.[6n.. |
---=== WRITE ===---
000000 1b 5b 31 3b 31 52 |.[1;1R |
---=== READ ===---
000000 0a 0a 0a 0a 0a 1b 5b 36 6e 1b 5b |......[6n.[ |
---=== WRITE ===---
000000 1b 5b 35 3b 31 52 |.[5;1R |
---=== READ ===---
000000 39 39 39 39 42 1b 5b 36 6e 1b 5b 72 |9999B.[6n.[r |
---=== WRITE ===---
000000 1b 5b 35 3b 31 52 |.[5;1R |
---=== READ ===---
000000 1b 5b 31 3b 39 39 39 39 72 0d 0d 0d 1b 5b 39 39 |.[1;9999r....[99|
000010 39 39 42 5b 61 64 6d 69 6e 40 73 77 69 74 63 68 |99B[admin@switch|
000020 5d 20 3e 20 |] > |
Что именно делает эта серия escape-последовательностей, и как лучше всего справиться с этим в моем клиенте?
from asyncio import Protocol, get_event_loop
from serial.aio import create_serial_connection
class Serial(Protocol):
def connection_made(self, transport):
self.transport = transport
def data_received(self, data):
self.buffer += data.decode("utf-8")
self.handle()
def send(self, line):
self.transport.write("{}\r\n".format(line).encode())
loop = get_event_loop()
coro = create_serial_connection(loop, Serial, "/dev/ttyUSB0")
loop.run_until_complete(coro)
loop.run_forever()
1 ответ
Этот кусок с [6n
(и предшествующий ему escape-символ) просит терминал сообщить ему, где находится курсор, как часть определения размера экрана. По-видимому pyserial
не понимает этого, и вам придется немного подождать, пока программа попросит отказаться и продолжить.
В последовательностях управления XTerm:
CSI Ps n Device Status Report (DSR).
...
Ps = 6 -> Report Cursor Position (CPR) [row;column].
где CSI - управляющая последовательность инициатора escape [
,
Когда ты бежишь screen
, он интерпретирует эту последовательность управления (то есть, он читает, понимает и отвечает соответственно):
case 'n': if (a1 == 5) / * Сообщить о состоянии терминала */ Отчет (победа, "\033[0n", 0, 0); иначе, если (a1 == 6) /* Сообщить о позиции курсора */ Отчет (победа, "\033[%d;%dR", победа->w_y + 1, победа->w_x + 1);
Последовательность управления отчетом о состоянии используется несколькими программами, такими как resize
определить размер экрана вашего терминала. Он работает, перемещая курсор в невозможный крайний правый нижний угол, а затем (поскольку терминалы имеют ограничения), спрашивая, как далеко он ушел. Этот конкретный пример не использует resize
, который отправляет 999
для каждой координаты (но это спорно, есть ли реальный сценарий, где необходимы четыре цифры).
Приложение, которое отправляет последовательность контроля состояния устройства, перемещает курсор вниз с помощью escape. [9999B
и, очевидно, использует это позже при настройке полей прокрутки с последовательностью, заканчивающейся на r
, Это часто используемая функция VT100 не в ECMA-48 (r
обозначает частную последовательность использования, т. е. не стандартизирована).
Если вы дадите ей фиктивное значение для позиции курсора, вы можете получить плохие результаты.
Он также пытается с помощью двух разных управляющих последовательностей переместить курсор к дому (вверху слева) в этой последовательности из screen
чтения / записи:
1b 5b 48 c4 9b 48
(0x9b
является 8-битным эквивалентом 0x1b
, 0x5b
или убежать [
).
Так что ваш выбор невелик:
- устранить (возможно, ненужный) запрос размера экрана, или
- измените свой сценарий pyserial для обработки последовательности контроля состояния устройства