行缓冲串行输入
问题描述
我有一个串行设备,我正在尝试从中读取输入.我给它发送了一个字符串ID",它返回ID XX"(其中是一个ASCII回车,十六进制0x0d).
I have a serial device that I'm trying to read input from. I sent it a string "ID", and it returns "ID XX" (where is an ASCII carriage return, hex 0x0d).
由于 serial.readline 上的 eol 选项不再受支持,我使用 TextIOWrapper 从串口读取并一次返回一行.
Since the eol option on serial.readline is no longer supported, I'm using TextIOWrapper to read from the serial port and return a line at a time.
我的问题是,它不是在看到回车后立即返回我的字符串,而是等到我打开串口时设置的超时时间的两倍.我希望它在读取整行后立即返回字符串,因为我可能有数百个这样的命令要发送到设备,而且我不想每次都等待超时.如果我将超时设置为 0,那么我根本没有输出(大概是因为我的脚本在设备有机会输出任何内容之前停止等待),如果我将超时设置为无,脚本将永远阻塞.
My problem is that instead of returning my string as soon as it sees the carriage return, it's waiting until the twice the timeout I set when I opened the serial port. I'd like it to return the string immediately as soon as it reads an entire line since I may have hundreds of these commands to send to the device and I don't want to wait for the timeout each time. If I set timeout to 0, then I get no output at all (presumably because my script stops waiting before the device has a chance to output anything), and if I set the timeout to None, the script blocks forever.
这是一个简单的测试脚本:
Here's a simple test script:
import serial
import io
import time
ser = serial.Serial("/dev/ttyUSB0", baudrate=9600,
bytesize=8, parity='N', stopbits=1,
xonxoff=0, rtscts=1, timeout=5)
sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser),
newline=None)
sio.write(unicode("ID"))
sio.flush()
print "reading..."
x = sio.readline()
print len(x)
print x
脚本从说正在读取"到打印从串行端口读取的ID XX"字符串总是需要 10 秒.
The script always takes 10 seconds from the time it says "reading" until it prints the "ID XX" string that it read from the serial port.
我确定设备正在输出回车,因为我使用 strace 来观看读取:
I'm certain that the device is outputting the carriage return, as I've used strace to watch the reads:
select(4, [3], [], [], {5, 0}) = 1 (in [3], left {4, 991704})
read(3, "I", 8192) = 1
select(4, [3], [], [], {5, 0}) = 1 (in [3], left {4, 999267})
read(3, "D", 8191) = 1
select(4, [3], [], [], {5, 0}) = 1 (in [3], left {4, 999420})
read(3, " ", 8190) = 1
select(4, [3], [], [], {5, 0}) = 1 (in [3], left {4, 999321})
read(3, "X", 8189) = 1
select(4, [3], [], [], {5, 0}) = 1 (in [3], left {4, 999355})
read(3, "X", 8188) = 1
select(4, [3], [], [], {5, 0}) = 1 (in [3], left {4, 999171})
read(3, "", 8187) = 1
select(4, [3], [], [], {5, 0}) = 0 (Timeout)
select(4, [3], [], [], {5, 0}) = 0 (Timeout)
您可以看到导致 10 秒延迟的 2 个 select() 超时,但您也可以清楚地看到正在读取的回车符.我尝试将换行参数设置为无"和"(应该自动允许 、 和 )和",但每次结果都相同.
You can see the 2 select() timeouts that give the 10 second delay, but you can also clearly see the carriage return being read. I've tried setting the newline parameter to 'None' and '' (which should automatically allow , , and ), and to '', but with the same result each time.
我还尝试将 BufferedRWPair() 调用中的 buffer_size 设置为 '1' 以防止它缓冲输入,但这没有任何区别.
I've also tried setting the buffer_size in the BufferedRWPair() call to '1' to keep it from buffering input, but that made no difference.
知道我做错了什么吗?
如果我不能让这个工作,我的下一步将是使用 serial.read() 一次读取一个字符并进行我自己的行缓冲,但我想尝试正确"地做到这一点首先使用 textiowrapper.
If I can't get this working, my next step will be to use serial.read() to read a character at a time and do my own line buffering, but I wanted to try to do it the "right" way with textiowrapper first.
解决方案
今天浪费了几个小时.结果发现,io.BufferedReader
一直读取到它的缓冲区填满,然后将缓冲区传递给 io.TextIOWrapper
.默认缓冲区大小为 8192,因此根据您的设备,这可能需要一段时间.
Wasted a few hours on this today. It turned out that io.BufferedReader
reads until it has filled its buffer and then passes the buffer to io.TextIOWrapper
. The default buffer size is 8192, so depending on your device this might take a while.
正确的示例代码应该是:
The correct example code should be:
# buffer size is 1 byte, so directly passed to TextIOWrapper
sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser, 1), encoding='ascii')
print sio.readline()[:-1]
相关文章