シリアル通信中のボタンの受付

解決


初心者  2008-05-08 10:17:31  No: 68272  IP: 192.*.*.*

お願いします。
環境はVisual C++ 2005 ダイアログベース MFCアプリケーションです。

ボタンを二つ用意し、一つがシリアル通信を開始するSTARTボタン、もう一つがそれを止めるSTOPボタンとします。
STARTボタンを押すとシリアル受信をします。
while(1){
  前処理
  ReadFile(Handle, data, 1, num, NULL);
  後処理
}
こんな感じです。
それで、STOPボタンが押されたらwhileから抜け出して処理を終了したいと思っています。
前処理のところにPeekMessageを入れてみたのですが、うまくSTOPボタンをおせません。
原因はCOMTIMEOUTS構造体の設定だと思います。
timeouts.ReadTotalTimeoutMultiplier = 500;  //1Byte毎のタイマ
timeouts.ReadTotalTimeoutConstant = 10;  //1関数コール毎のタイマ
これで、ReadFileのところで500ms待ちますよね?
それが原因だと思うのですが、この設定を変更できない場合、STOPボタンを認識することは可能でしょうか?
現在は変更を設定できるか調査中ですが、今後同じようなことが起きたときの為にも勉強したいと思っております。

よろしくお願いします。

編集 削除
tetrapod  2008-05-08 10:33:13  No: 68273  IP: 192.*.*.*

フツーはこういう場合にはマルチスレッドにする
UIスレッドと別にシリアル送受信スレッドを用意するのがいい

編集 削除
そだ  2008-05-08 12:18:05  No: 68274  IP: 192.*.*.*

>timeouts.ReadTotalTimeoutMultiplier = 500; //1Byte毎のタイマ
>timeouts.ReadTotalTimeoutConstant = 10; //1関数コール毎のタイマ
>これで、ReadFileのところで500ms待ちますよね?
これはTimeoutだから通信が途切れたりハングアップしたときに
500×通信量ms分、再度通信できないか待つのであって
プロセスそのものを止めるものではないんじゃないかなぁ。
(以前もにたようなのがあったような)

明示的にメインスレッドを止めたいならSleep関数を使うべき。
あとtetrapodさんの意見に同意。負荷がかかったり、
ハングアップする可能性のあるものは別スレッドで実行した方がいいと思う。

編集 削除
初心者  2008-05-08 14:38:30  No: 68275  IP: 192.*.*.*

tetrapodさん、そださん回答ありがとうございます。

マルチでできれば良いんですが、現状、このままでやるしかないです(T_T)

>これはTimeoutだから通信が途切れたりハングアップしたときに
>500×通信量ms分、再度通信できないか待つのであって
>プロセスそのものを止めるものではないんじゃないかなぁ。
そうでした。。。勘違いしてました。
ただ、そうするとReadFileでは1文字ずつ受信してるので、
バッファに10文字くらい積まれてるとすると10回はPeekMessageを通ってることになりますよね?
ちなみにバッファに積まれた文字を全て受信したらwhileループを抜けるようにしてあります。
うまくSTOPボタンを拾えないのはPeekMessageの理解が足りないだけですかね^^;;??

編集 削除
そだ  2008-05-08 17:28:30  No: 68276  IP: 192.*.*.*

>うまくSTOPボタンを拾えないのはPeekMessageの理解が足りないだけですかね^^;;??
シングルスレッドで動いているので送受信でCPU負荷がかかり
メッセージキューを処理できない状態になっているのか、
メッセージ処理中にPeekMessageが呼びだせないとか、
PeekMessageに渡す引数間違えているとか・・・

PeekMessageの代わりにGetMessageを使って
動くか試してみたらどうでしょうか。

編集 削除
初心者  2008-05-09 17:42:19  No: 68277  IP: 192.*.*.*

そださんありがとうございます。

正確にはSTOPボタンを拾えないのではなく、拾うまでに時間がかかっているようです。
GetMessageにしてみても結果はかわりませんでした。

こんなサンプルを作ってみました。

STARTボタンを押したとき
{
  start_time_2 = GetTickCount();
  while(1){
    start_time_1 = GetTickCount();
    while(1){
      time_1 = GetTickCount() - start_time_1;
      if(time_1 > 500)break;
    }
    if(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
      ::TranslateMessage(&msg);
      ::DispatchMessage(&msg); 
    }
    count++;
    if(flg == 1){
      AfxMessageBox("STOPボタン確認");
      flg = 0;
      return;
    }
    time_2 = GetTickCount() - start_time_2;
    if(time_2 > 20000)break;
  }  
  AfxMessageBox("test終了");
}

STOPボタンを押したとき
{
  flg = 1;
}

処理中はSTOPボタン以外を押せないようにして、STOPされたら復帰させています。
このサンプルで確認したところ、START後、すぐにSTOPボタンを押しても認識するのが遅いです。
例えばスタート1秒後位にSTOPボタンを押しても認識するのは10秒後とかです。
やはり原因はPeekMessageですか?

編集 削除
初心者  2008-05-09 17:45:08  No: 68278  IP: 192.*.*.*

追記です。

>処理中はSTOPボタン以外を押せないようにして、STOPされたら復帰させています。
この1文は間違えました。無視してください。

コードを見やすいようにTABで字下げしたのにできないみたいですね…orz
見にくくてすいません(>_<)

編集 削除
どら  2008-05-09 18:50:08  No: 68279  IP: 192.*.*.*

>マルチでできれば良いんですが、現状、このままでやるしかないです(T_T)

それはなぜでしょう?
マルチスレッドにする方法がわからないから・・・というのであれば勉強した方がよいような・・・。

>コードを見やすいようにTABで字下げしたのにできないみたいですね…orz
スペースを使えばそれなりにできますよ^^
僕は大抵その様にしています。

>このサンプルで確認したところ、START後、すぐにSTOPボタンを押しても認識するのが遅いです。

STOPボタンを押してから、そのフラグを認識するまでに時間がかかっているだ
けじゃないんですか?
一度フラグを確認してから、もう一度フラグを確認するまでの最短時間はデバ
ッグしてみたり・・・

ループをフラグ使って抜け出すようにする場合、よくある話ですよ。
例えば、関数の処理失敗までのタイムアウトとか、処理そのものに時間がかか
ったりとか・・・

編集 削除
そだ  2008-05-11 11:00:55  No: 68280  IP: 192.*.*.*

>やはり原因はPeekMessageですか?
PeekMessageそのものの動作速度はたかが知れてるけど
その周りの処理が重いからボタンのメッセージが入っても
レスポンスが遅いのかな。
PeekMessageを使っていてもシングルスレッドの中に
処理の重いループ作るとどうしてもメッセージ処理の
レスポンスは悪くなりますよん。

編集 削除
rin  2008-05-11 20:04:28  No: 68281  IP: 192.*.*.*

内側のループから出たときにしかメッセージを処理しないからでは?

編集 削除
...  2008-05-12 09:46:02  No: 68282  IP: 192.*.*.*

STARTボタンを押したとき
{

    flg = 0;    // 初期化

    DWORD start_time_2 = GetTickCount();
    while(
        ((GetTickCount() - start_time_2) < 10000000) /* 20000 * 500 */
        ||(!flg)) {
        if(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg); 
        }
    }
    AfxMessageBox(((flg) ? "STOPボタン確認" : "test終了"));
}

STOPボタンを押したとき
{
    flg = 1;
}

編集 削除
初心者  2008-05-12 12:06:12  No: 68283  IP: 192.*.*.*

皆様

回答ありがとうございます。


>STOPボタンを押してから、そのフラグを認識するまでに時間がかかっているだけじゃないんですか?
STOPボタンを押したとき(flg=1;)のところでブレークかけてみたんですが、
ココに入るのが遅いです。
ボタンを押してからそれを認識するまでに時間がかかっているようです。


>内側のループから出たときにしかメッセージを処理しないからでは?
内側のループはReadFileで受信できなかったときを想定して作ってみましたので、
内側のループ内では処理をしないようにしました。


ボタンを押すと、画面上で押したことがわかる(ボタンが凹む)と思いますが、
それ自体が遅れます。。。
内側のループを削除できないとすると遅れるのは回避できないのでしょうか?

ちなみにReadFileで正常に受信できるときはSTOPできました。
ありがとうございました。

編集 削除
gak  2008-05-12 12:56:48  No: 68284  IP: 192.*.*.*

> if(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
  ↓
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){

にしてみ。少しはマシになるだろう。
まぁ、マルチスレッド化する事がbestなのは変わらないけど。

編集 削除
rin  2008-05-12 13:37:31  No: 68285  IP: 192.*.*.*

>>内側のループから出たときにしかメッセージを処理しないからでは?
>内側のループはReadFileで受信できなかったときを想定して作ってみましたので、
>内側のループ内では処理をしないようにしました。
内側のループをでたときに、
それまでにたまった分のメッセージを全部処理するようにしないといけない。
金曜のソースでは、一個分しか処理しない

編集 削除
初心者  2008-05-23 10:43:29  No: 68286  IP: 192.*.*.*

皆さま

いろいろとアドバイスを頂いたのに遅くなって申し訳ありません。
結局のところ、反応は鈍いですが、STOPを確認できるので、それでよしとしました。

gakさん、rinさんのご指摘を試してもみましたらうまくいかず、、、

これからはプログラムを作る前にマルチスレッドでできるようなアルゴリズムを考えるようにします。

まだまだわからないことだらけですが、いろいろ勉強して早く皆さんのようになれるよう頑張りたいと思います。

やさしくアドバイス頂きありがとうございました。

これからもいろんな方の質問とその回答も拝見させて頂き勉強させて頂きます。
また、困ったときにはよろしくお願い致します。

ありがとうございました。

編集 削除