转义序列中断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                                            |] >             |

这一系列转义序列到底在做什么,在我的客户端中处理这一问题的最佳方法是什么?

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("{}rn".format(line).encode())
loop = get_event_loop()
coro = create_serial_connection(loop, Serial, "/dev/ttyUSB0")
loop.run_until_complete(coro)
loop.run_forever()

带有[6n(及其前面的转义符)的块要求终端告诉它光标在哪里,作为确定屏幕大小的一部分。显然pyserial不理解这一点,你必须等待一段时间,等待程序要求放弃并继续。

在XTerm控制序列中:

CSI Ps n  Device Status Report (DSR).
...
Ps = 6  -> Report Cursor Position (CPR) [row;column].

其中CSI是控制序列启动器escape[

当您运行screen时,它解释此控制序列(即,它读取理解适当回复):

案例'n':if(a1==5)/*报告终端状态*/报告(获胜,"\033[0n",0,0);否则,如果(a1==6)/*报告光标位置*/报告(win,"\033[%d;%dR",win->w_y+1,win->w _x+1);

状态报告控制序列由resize等少数程序用于确定终端屏幕的大小。它的工作方式是将光标移动到不可能的右下角,然后(因为终端有限制)问它走了多远。这个特定的例子没有使用resize,它为每个坐标发送999(但是否存在需要四位数字的真实场景存在争议)。

发送设备状态控制序列的应用程序使用转义符[9999B并且在稍后设置以r结束的序列的滚动边距时显然会使用该光标。这是ECMA-48中经常使用的VT100功能而非(r表示专用序列,即未标准化)。

如果你给它一个虚假的光标位置值,你可能会得到糟糕的结果。

它还尝试使用两个不同的控制序列将光标从screen读/写:移动到此序列中的主页(左上角)

1b 5b 48 c4 9b 48

(0x9b0x1b0x5b转义[

所以你的选择很少:

  • 消除对屏幕大小的(可能不必要的)查询,或者
  • 修改pyserial脚本以处理设备状态控制序列

最新更新