2016-05-27 26 views
2

an answer on another questionでは、QSerialPortでメインのQtイベントループを使用すると、QSerialPort::waitForBytesWrittenが別のイベントループを開くため、「微妙なエラー」が発生する可能性があると主張しています。イベントループでQSerialPort :: waitForBytesWritten "微妙なエラー"?

すべてのバイトが書き込まれるまでメインイベントループが再入力されないように、waitForBytesWrittenメソッドを使用しています。これは有効な方法ではありませんか?どのような「微妙なエラー」が発生する可能性がありますか? flush()を代わりに使用する必要があります。これにより、保留中のバイトがすべて書き込まれるまで、ではなく、が再び入力されます。

I 午前そのシリアルポートの通信に関連した自分のアプリケーションでいくつかの奇妙な行動を見て、私はコミュニケーションの「層」は、問題を引き起こしているかわからないんだけど、それはも、私の使用に関連していなくてもよいですQSerialPort。現時点では、私が起源について唯一のヒントはがすぐに私が貧しい行動の開始を見る前にResourceErrorを放出するということです。このResourceErrorは私のwaitForBytesWrittenの使用に関連していますか?

私はDebian 7システムでQt 5.5.1を使用しています。 Unixでは

+0

「ResourceError」が何らかのコードを見ることなくなぜ起こるのかを知るのは難しいです。 –

+0

@KubaOber非常に複雑なアプリケーションであり、プロプライエタリな製品の一部であるため、このケースでどれくらいのコードを提供できるかはわかりません。私がいくつかの小さな例に絞り込んだとしても、どの部分が関連しているか分からず、通信層、シリアル通信チャネルの反対側のプロセスなどが最小限の再現可能なケースを作成できませんすべての所有権。 –

+0

...しかし、正確には、ハードウェアのシリアルポートで 'ResourceError'を引き起こすことさえも助けになるでしょう。 –

答えて

1

、開封通知はResourceErrorEAGAINEIO、およびEBADFをマッピングし、あなたがwaitFor機能を呼び出したり、ポートを再度開くまでまで、さらに読み取り通知の処理を停止します。これにより、waitForを開くまで、QSerialPortは読み込みに役に立たなくなります。おそらく同じエラーにEIOEBADFをマップするのは理にかなっています。しかし、EAGAINは良性なので、そのように扱うにはQtバグのように見えます。

それが問題だ場合、あなたはエラーをクリアしてwaitForReadyRead(0)またはwaitForBytesWritten(0)を再呼び出しし、エラーが再び表示された場合に見ることができる表示するには:

bool retryWaitForBytesWritten(int n, int retries = 10) { 
    if (!dev.bytesToWrite()) return false; 
    while (retries-- && !dev.waitForBytesWritten(n)) { 
    if (dev.error() != QSerialPort::ResourceError) 
     return false; 
    dev.clearError(); // retry if it was a resource error 
    } 
    return true; 
} 

ので、あなたは主にwaitForBytesWrittenを使用しないでください。

  1. おそらくあなたが思っていることはしません。保証することは、writeシステムコールを使用してオペレーティングシステムにデータを供給することによってQSerialPortの内部書き込みバッファが空になったことだけです。これは、データが物理的に送信されたことを意味するものではありません。

  2. 潜在的にブロックされます。ブロックすることは保証されていません。

  3. 通常、書き込みが完了したときの通知は必要ありません。ハーフデュプレックス(本質的に)のコマンド応答プロトコルを使用している場合は、送信が終了した時点ではなく、応答が到着したかタイムアウトになったかは気にしません。

    ポーリングコマンドを送信し、デバイスが消費するよりも速く生成したくないストリーミングプロトコルを使用している場合は、コマンドを発行してデータ構造内で「発行済み」とマークする必要があります。 bytesToWriteは最高透かしを超えています。 bytesWrittenシグナルによって、送信バッファが低い透かしになっていることが通知されるまで、新しいコマンドの追加を中断します。 readyReadスロットは、発行されたコマンドへの応答と一致し、それらを適切に示します。

あなたのコードには、の呼び出しが含まれることが理想的です。私たちの周りの世界は非同期です。あなたは物事を待たずに、に反応します。何かが起きるまでイベントループを止めないでください。それは逆です。あなたが待っていることの前に他のことが起きるかもしれません。あなたはそれまで無視しています。 I/Oを別のスレッドにプッシュしても、ブロックするだけでスレッド全体を無駄にしてしまいます。制御の流れをリダイレクトするイベントを処理することは、順次疑似同期コードでは困難です。すべてが(必要な!)エラーチェックで詰まってしまいます。多くの場合、人々は例外を使用することに頼っています。これは問題ありませんが、大規模なコードベースでは正しく実行するのが難しく、コールスタックにないコードにコントロールをリダイレクトすることを望む場合もあります。着信データやエラーなどの非同期イベントに直面したときに、階層的な状態マシンとして明示的に表現するときに、コードの正確性を判断する方がずっと簡単です。

非同期的に発生するイベントに基づいてシステムが進行するようにシステムを設計します。 UIのいくつかの側面が送信されるデータの状態を示す必要がある場合は、単にそれを次のようにしてください:待機しないで、スレッドをブロックしないでください。

This answerは、ステートマシンフレームワークを使用してQIODeviceに基づくデバイスの非同期I/Oを実装する方法を示しています。状態を設定したら、UIの動作に関連付けるのは比較的簡単です。これらの回答を参照してください。onetwothreefourfivesixseveneight

また、私は間違っていて、私はもう一つの答えを修正しました。 QSerialPortはイベントループを再入力せず、ネットワークモジュールのソケットも再投入しません。

+0

私はなぜあなたが "ブロックするので"関数を使うのは常に*間違っていると言っているのか分かりません。それは*なぜ*私はそれを使用しています。バイトが書き込まれるまでブロックしたい。私はこれを8〜10バイト程度(通常は4バイト)しか書きません。文字通りすぐに書かれたバイトを欲しがります。エラーがあればすぐに知りたいです。私のアプリケーションでは、タイマーなどを含む 'waitForBytesWritten'呼び出しを行う時に、これらのバイトを書くことよりも重要なことは他に何もないと私を信じてください。 –

+0

@KyleStrandそれは大丈夫です。とにかくあなたの問題。本当のQtバグを打っていると思います。あなたは非同期でコードを書くことはスタイルの問題であると主張することができます。私の経験では、あなたのコミュニケーションが些細なものでなくなるとすぐに恐ろしいスパゲティにつながるということです。単純なプロトコルであっても、非同期の方法はより宣言的なスタイルにつながります。しかし、それはここのポイントの横にある。 –

+0

これはこれまでのところ非常に参考になりました。私はあなたがEAGAINについて正しいと思います。これは、非同期の読み込み操作が完了した時点で読み込めるものがないことを意味します。 (または...そのようなものですか?)あなたがまだ答えていない部分があります。小さな "書き込み"操作をブロックしたいと仮定した場合、 'waitForBytesWritten'より' flush'が好ましいですか? –