7

入力を読み取ろうとしているシリアルデバイスがあります。「ID\r」という文字列を送信すると、「ID XX\r」が返されます (\r は ASCII キャリッジ リターンで、16 進数の 0x0d です)。

serial.readline の eol オプションはサポートされなくなったため、TextIOWrapper を使用してシリアル ポートから読み取り、一度に 1 行ずつ返します。

私の問題は、改行が表示されるとすぐに文字列を返すのではなく、シリアルポートを開いたときに設定したタイムアウトの 2 倍になるまで待機していることです。デバイスに送信するこれらのコマンドが何百もある可能性があり、毎回タイムアウトを待ちたくないので、行全体を読み取るとすぐに文字列を返すようにしたいと思います。タイムアウトを 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

スクリプトは、「読み取り中」と表示されてから、シリアル ポートから読み取った「ID XX」文字列を出力するまで、常に 10 秒かかります。

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)

10 秒の遅延を与える 2 つの select() タイムアウトを確認できますが、キャリッジ リターンが読み取られていることもはっきりと確認できます。改行パラメータを 'None' と '' (\r、\n、および \r\n を自動的に許可する必要があります)、および '\r' に設定しようとしましたが、毎回同じ結果になります。

また、BufferedRWPair() 呼び出しで buffer_size を '1' に設定して、入力をバッファリングしないようにしようとしましたが、違いはありませんでした。

私が間違っていることは何か分かりますか?

これが機能しない場合、次のステップは、serial.read() を使用して一度に 1 文字ずつ読み取り、独自の行バッファリングを行うことですが、textiowrapper を使用して「正しい」方法でそれを実行したかったのです。最初。

4

4 に答える 4

8

今日はこれで数時間を無駄にしました。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]
于 2012-09-25T10:39:24.443 に答える
3

警告: 私は Mac で Python 3.4 を使用しているため、走行距離は異なる場合がありますが、Python 2.7 にバックポートされた TextIOWrapper を使用すると、Python 2.7 (およびその他の OS) の状況は、以下で説明するものと基本的に同じになります。

主な問題は、io.TextIOWrapper 自体が、文書化されていない _CHUNK_SIZE 属性によって制御されるバッファリング メカニズムを使用していることです。これはかなり不快です。したがって、次の 2 つの選択肢があります。

  1. 試したときにタイムアウトを使用します。これは、pyserial ドキュメント ページのreadlineのドキュメントで示唆されていることです。ただし、(実際に行ったように) 大きな値を使用すると、TextIOWrapper のバッファーを満たすのに十分なデータがない場合、コードはタイムアウトに達するまでブロックされます。これは本質的にあなたが経験していることです(タイムアウト値の2倍を待たなければならない理由は知りませんでしたが、これは TextIOWrapper の実装を見ることで整理でき、最終的にはあなたの質問には関係ないと思います)。
  2. 2 番目の選択肢は、_CHUNK_SIZE を 1 に変更することです。あなたの場合は、次の行を追加するだけです。

    sio._CHUNK_SIZE = 1
    

    sio を初期化した直後にコードに追加します。これにより、TextIOWrapper 自体のバッファリングがオフになるという不快な結果が生じる可能性があります (これは、入力のインクリメンタル デコードに使用されます)。パフォーマンスが問題にならない場合、これが最も簡単な解決策です。パフォーマンスが問題になる場合は、_CHUNK_SIZE に触れずに、タイムアウトの値を低く設定できます。ただし、この場合、readline() から空の文字列を取得する準備をしてください (デバイスが空の行を送信した場合、それは '\n' として送信されるため、次の場合に取得する空の文字列と区別できます)。読み取りが割り当てられた時間を使い果たします)。

コードには別の問題があります: sio が削除されると、ser の close メソッドが 2 回呼び出され、プログラムが終了しようとしているときに例外が発生します (少なくとも、コードを試してみるとこれが発生します)私のコンピューターで)。serial.Serial のインスタンスを作成し (そう思われます)、それらを BufferedRWPair に渡す必要があります。

また、TextIOWrapper に基づいたラッパー クラスも作成しました。興味があれば投稿することもできますが、厳密に言えば、必要のない余分なコードで応答を散らかしたくありませんでした。

PS: その間、Ubuntu でコードを試してみました。私の Mac では、io.BufferedRWPair のバッファー サイズを 1 に設定する必要はありませんでしたが、Ubuntu では、_CHUNK_SIZE を 1 に設定するだけでなく、これも行う必要がありました。

于 2015-01-12T02:20:22.140 に答える
0

キースのコードに感謝しますが、私はこのコードをある程度移植できるようにしたかったので、デフォルトの「シリアル」パッケージを使い続けたいと思います。

さらに、私はまだ Python を学んでいるので、意図した方法で TextIOWrapper を使用する方法を学びたいと思いました。

serial.readline() を機能させるのをあきらめたので、今のところ、単純な「readLine」関数を使用して一度に 1 文字ずつ読み取り、キャリッジ リターン ターミネータを探します。ただし、さらに奇妙なことに遭遇した場合は、あなたのコードを使用して再訪する可能性があります。

ありがとう!

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

    #"print "str = " + str

    return str
于 2012-04-20T14:37:19.867 に答える
0

これを実際に見に行かないとデバッグするのは難しいでしょう。ただし、私の tty モジュールを使用できるかどうかを確認してください。

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

そこで SerialPort オブジェクトを試してください。これを使用して、「他のシリアルモジュール」にあなたが説明したのと同様の問題がたくさんあったシリアル機器とやり取りすることに成功しました。これにより、FIFO にデータがあるかどうかもわかります。

それがどうなるか今私にさせてください。

于 2012-04-19T07:08:41.743 に答える