2012-04-19 77 views
7

我有一個串行設備,我試圖從中讀取輸入。我向它發送了一個字符串「ID \ r」,並且它返回「ID XX \ r」(其中\ r是一個ASCII回車,十六進制0x0d)。線路緩衝串行輸入

由於不再支持serial.readline上的eol選項,我使用TextIOWrapper從串行端口讀取並一次返回一行。

我的問題是,一旦它看到回車,而不是返回我的字符串,它會等到我打開串口時設置的兩倍超時。我希望它在讀取整行時立即返回字符串,因爲我可能會有數百個這樣的命令發送到設備,而且我不想每次都等待超時。如果我將超時設置爲0,則根本沒有輸出(大概是因爲我的腳本在設備有機會輸出任何內容之前停止等待),並且如果將超時設置爲無,腳本將永遠阻塞。

這裏有一個簡單的測試腳本:

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\r")) 
sio.flush() 

print "reading..." 

x = sio.readline() 

print len(x) 
print x 

腳本總是需要10秒,從它說:「讀」,直到它打印「ID XX」的字符串,它從串口讀取時間。

我敢肯定,該設備輸出回車,因爲我用strace的觀看寫着:

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, "\r", 8187)      = 1 
select(4, [3], [], [], {5, 0})   = 0 (Timeout) 
select(4, [3], [], [], {5, 0})   = 0 (Timeout) 

你可以看到2選擇()超時,讓10秒延遲,但您也可以清楚地看到正在讀取的回車。我嘗試將newline參數設置爲'None'和''(它應該自動允許\ r,\ n和\ r \ n),並設置爲'\ r',但每次都有相同的結果。

我也嘗試將BufferedRWPair()調用中的buffer_size設置爲'1',以防止緩衝輸入,但這沒有什麼區別。

任何想法我做錯了什麼?

如果我不能得到這個工作,我的下一步將是使用serial.read()來一次讀取一個字符,並做我自己的行緩衝,但我想嘗試做到這一點「右「先用textiowrapper的方式。

+0

您確定打印語句是不是觸發輸出緩衝?嘗試使用-u運行它 – 2012-04-19 07:18:19

回答

0

如果沒有真正看到它,這將很難調試。但看看你是否可以使用我的tty模塊。

http://code.google.com/p/pycopia/source/browse/trunk/aid/pycopia/tty.py

嘗試通過一個串口對象在那裏。我已經成功地將它用於與串行儀器進行交互,其中「其他串行模塊」存在與您描述的類似的許多問題。這個人也可以告訴你,如果你有FIFO中的數據。

讓我現在怎麼樣。

0

感謝代碼Keith,但我想保持這個代碼有點便攜,所以我想堅持默認的「串行」包。

另外,由於我還在學習Python,我想嘗試學習如何使用TextIOWrapper的方式。

我放棄了嘗試使serial.readline()工作,所以現在我只使用一個簡單的「readLine」函數來讀取一個字符,並尋找一個回車終止符。儘管如果我遇到更多的串行古怪,我可能會重新使用你的代碼。

謝謝!

def readLine(ser): 
    str = "" 
    while 1: 
     ch = ser.read() 
     if(ch == '\r' or ch == ''): 
      break 
     str += ch 

    #"print "str = " + str 

    return str 
6

今天浪費了幾個小時。原來io.BufferedReader讀取,直到它填滿了它的緩衝區,然後將緩衝區傳遞給io.TextIOWrapper。默認緩衝區大小爲8192,因此根據您的設備而定,這可能需要一段時間。

正確的示例代碼應該是:

# buffer size is 1 byte, so directly passed to TextIOWrapper 
sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser, 1), encoding='ascii') 
print sio.readline()[:-1] 
2

警告:我使用Python 3.4在Mac上使您的里程可能會有所不同,但我相信,隨着TextIOWrapper回遷到Python 2.7,在Python 2.7的情況(和其他操作系統)將基本上與我在下面描述的相同。

主要問題是io.TextIOWrapper本身使用緩衝機制,由未記錄的_CHUNK_SIZE屬性控制。這很不愉快。所以你有兩種選擇:

  1. 在試用時使用超時。這是在pyserial文檔頁面的readline文檔中暗示的。但是,如果您使用較大的值(如您所做的那樣),那麼當沒有足夠的數據填充TextIOWrapper的緩衝區時,您的代碼將會阻塞,直到達到超時。這就是你本質上遇到的(我沒有追求爲什麼你必須等待超時值的兩倍,但我認爲這可以通過查看TextIOWrapper的實現來解決,並最終與你的問題無關)。
  2. 第二個選擇是改變_CHUNK_SIZE 1。在你的情況下,只需將線

    sio._CHUNK_SIZE = 1 
    

    添加到您的代碼初始化串口之後。這有可能是令人不愉快的效果,即TextIourapper本身內的緩衝將被關閉(這用於輸入的增量解碼)。如果性能不是問題,這是最簡單的解決方案。如果性能出現問題,您可以設置較低的超時值,而不是_CHUNK_SIZE。但是,在這種情況下,準備從readline()中獲得一個空字符串(如果設備向您發送了一個空行,它將作爲'\ n'出現,因此它可以與您將在讀出的時間不足)。

還有一個問題是你的代碼:當SIO將被刪除,SER的close方法將被調用兩次,這將導致一個異常時,你的程序將是即將完成(至少這是如果我在我的電腦上試用您的代碼會發生什麼情況)。您應該創建(似乎)serial.Serial的實例並將其傳遞給BufferedRWPair。

我也創建了一個基於TextIOWrapper的包裝類,如果有興趣,我也可以發佈它,只是我不想亂扔一些額外的代碼,嚴格來說,這是不需要的。 PS:與此同時,我已經在Ubuntu上進行了試驗。雖然在我的Mac上,我沒有看到需要將io.BufferedRWPair的緩衝區大小設置爲1,但在Ubuntu上我也必須這樣做,除了將_CHUNK_SIZE設置爲1之外。