2012-04-19 38 views
7

Tôi có thiết bị nối tiếp mà tôi đang cố đọc đầu vào. Tôi đã gửi nó một chuỗi "ID \ r", và nó trả về "ID XX \ r" (trong đó \ r là một trở về vận chuyển ASCII, hex 0x0d).Đầu vào nối tiếp được lưu vào bộ đệm dòng

Vì tùy chọn eol trên serial.readline không còn được hỗ trợ, tôi đang sử dụng TextIOWrapper để đọc từ cổng nối tiếp và trả về một dòng tại một thời điểm.

Vấn đề của tôi là thay vì trả lại chuỗi của tôi ngay sau khi nó nhìn thấy vận chuyển trở lại, nó chờ đợi cho đến khi hai lần thời gian chờ tôi đặt khi tôi mở cổng nối tiếp. Tôi muốn nó trả về chuỗi ngay lập tức ngay khi nó đọc toàn bộ dòng vì tôi có thể có hàng trăm lệnh này để gửi tới thiết bị và tôi không muốn chờ thời gian chờ mỗi lần. Nếu tôi đặt thời gian chờ là 0, thì tôi không có đầu ra nào cả (có lẽ vì tập lệnh của tôi ngừng chờ trước khi thiết bị có cơ hội xuất bất kỳ thứ gì) và nếu tôi đặt thời gian chờ thành Không, tập lệnh sẽ chặn vĩnh viễn.

Dưới đây là một kịch bản thử nghiệm đơn giản:

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 

Kịch bản luôn mất 10 giây kể từ khi nó nói "đọc" cho đến khi nó in "ID XX" chuỗi mà nó đọc từ cổng nối tiếp.

Tôi chắc chắn rằng thiết bị được xuất ra trở về vận chuyển, như tôi đã sử dụng strace để xem lần đọc:

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) 

Bạn có thể thấy 2 lựa chọn() timeout mà cung cấp cho sự chậm trễ 10 giây , nhưng bạn cũng có thể thấy rõ sự trở lại của xe ngựa đang được đọc. Tôi đã thử đặt tham số dòng mới thành 'Không' và '' (sẽ tự động cho phép \ r, \ n và \ r \ n), và thành '\ r', nhưng với cùng một kết quả mỗi lần.

Tôi cũng đã thử thiết lập buffer_size trong lời gọi BufferedRWPair() thành '1' để giữ cho nó khỏi đầu vào đệm, nhưng điều đó không có sự khác biệt.

Bất kỳ ý tưởng nào tôi đang làm sai?

Nếu tôi không thể làm việc này, bước tiếp theo của tôi sẽ là sử dụng serial.read() để đọc một ký tự tại một thời điểm và làm theo dòng đệm của riêng tôi, nhưng tôi muốn cố gắng làm điều đó "cách với textiowrapper đầu tiên.

+0

Bạn có chắc chắn lệnh in không kích hoạt bộ đệm đầu ra? Hãy thử chạy nó với -u –

Trả lời

0

Điều này sẽ khó gỡ lỗi mà không thực sự ở đó để xem. Nhưng xem bạn có thể sử dụng mô-đun tty của tôi không.

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

Hãy thử đối tượng SerialPort trong đó. Tôi đã sử dụng thành công điều này để tương tác với các công cụ nối tiếp, trong đó "mô-đun nối tiếp khác" có rất nhiều vấn đề tương tự với những gì bạn mô tả. Điều này cũng có thể cho bạn biết nếu bạn có dữ liệu trong FIFO.

Hãy để tôi làm cách nào.

0

Cảm ơn mã Keith, nhưng tôi muốn giữ mã này hơi di động, vì vậy tôi muốn gắn bó với gói "nối tiếp" mặc định.

Ngoài ra, vì tôi vẫn đang học Python, tôi muốn cố gắng tìm hiểu cách sử dụng TextIOWrapper theo cách được dự định.

Tôi đã từ bỏ việc cố gắng tạo công việc serial.readline(), vì vậy bây giờ tôi sẽ chỉ sử dụng một hàm "readLine" đơn giản để đọc một ký tự tại một thời điểm và tìm một trình kết thúc trả về vận chuyển. Mặc dù nếu tôi gặp phải nhiều kỳ quặc hơn, tôi có thể truy cập lại bằng mã của bạn.

Cảm ơn!

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

    #"print "str = " + str 

    return str 
6

Đã bỏ lỡ vài giờ vào ngày hôm nay. Nó bật ra rằng io.BufferedReader lần đọc cho đến khi nó đã điền vào bộ đệm của nó và sau đó vượt qua bộ đệm để io.TextIOWrapper. Kích thước bộ đệm mặc định là 8192, do đó tùy thuộc vào thiết bị của bạn, việc này có thể mất một lúc.

Ví dụ mã đúng nên là:

# 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

Nên biết trước: Tôi đang sử dụng Python 3.4 trên máy Mac mileage của bạn có thể thay đổi, mặc dù tôi tin rằng với TextIOWrapper backported để Python 2.7, tình hình trong Python 2.7 (và các hệ điều hành khác) về bản chất sẽ giống như những gì tôi mô tả bên dưới.

Vấn đề chính là bản thân io.TextIOWrapper sử dụng cơ chế đệm, được kiểm soát bởi thuộc tính _CHUNK_SIZE không có giấy tờ. Điều này khá khó chịu. Vì vậy, bạn có hai lựa chọn:

  1. Sử dụng thời gian chờ khi bạn thử. Đây là những gì được gợi ý trong tài liệu của readline trên trang tài liệu giả dược. Tuy nhiên, nếu bạn sử dụng một giá trị lớn (như bạn đã làm), khi không có đủ dữ liệu để lấp đầy bộ đệm của TextIOWrapper, mã của bạn sẽ chặn cho đến khi hết thời gian chờ. Đây là những gì bạn đang trải nghiệm cơ bản (tôi đã không đi sau tại sao bạn phải đợi gấp đôi giá trị timeout, nhưng tôi nghĩ rằng điều này có thể được sắp xếp bằng cách nhìn vào việc thực hiện TextIOWrapper và cuối cùng là không liên quan đến câu hỏi của bạn).
  2. Sự lựa chọn thứ hai là để thay đổi _CHUNK_SIZE để 1. Trong trường hợp của bạn, bạn chỉ cần thêm dòng

    sio._CHUNK_SIZE = 1 
    

    mã của bạn ngay sau khi bạn khởi SIO. Điều này có hiệu ứng có lẽ khó chịu rằng việc đệm trong chính TextIOWrapper sẽ bị tắt (điều này được sử dụng để giải mã gia tăng đầu vào). Nếu hiệu suất không phải là một vấn đề, đây là giải pháp đơn giản nhất. Nếu hiệu suất là một vấn đề, bạn có thể đặt giá trị thời gian chờ thấp, không được buộc _CHUNK_SIZE. Tuy nhiên, trong trường hợp này được chuẩn bị để lấy một chuỗi rỗng từ readline() (nếu thiết bị gửi cho bạn một dòng trống, nó sẽ đi qua là '\ n', vì vậy nó có thể được phân biệt với chuỗi rỗng mà bạn sẽ nhận được khi một lần đọc hết thời gian được phân bổ).

Có vấn đề khác với mã của bạn: Khi sio bị xóa, phương pháp đóng sẽ được gọi hai lần, điều này sẽ dẫn đến ngoại lệ khi chương trình của bạn sắp hoàn thành (ít nhất là điều gì sẽ xảy ra nếu tôi thử mã trên máy tính của tôi). Bạn nên tạo (có vẻ như) cho các trường hợp serial.Serial và chuyển chúng vào BufferedRWPair.

Tôi cũng đã tạo một lớp trình bao bọc dựa trên TextIOWrapper, mà tôi cũng có thể đăng bài, nếu có sự quan tâm, chỉ là tôi không muốn phản ứng xả rác với một số mã bổ sung mà, nói đúng là không cần thiết.

PS: Trong khi đó, tôi đã thử nghiệm với mã trên Ubuntu. Trong khi trên máy Mac của tôi, tôi không thấy cần phải thiết lập kích thước bộ đệm của io.BufferedRWPair thành 1, trên Ubuntu, tôi cũng phải thực hiện điều này, ngoài việc đặt _CHUNK_SIZE thành 1.

Các vấn đề liên quan