MScommの受信待ちLoop中のエラーを解決するには?

解決


GTB  2006-03-16 01:45:13  No: 130647

機器との通信でMSCommコントロールを使用しています。

プログラムの内容は、機器が持っているID(値)を読み出すために
こちらからコマンドを送り、機器が返す値を受けるものです。

この値はズラズラと返って来るのですが、機器によって、桁数(文字数)や
その時間も異なります。
そのため、InBufferCountで一律に規定出来ないと思ったので、以下のように
ある程度時間をおいてから取り込むようにしています。
なお、RThreshold=1にしています。

(1)'受信またはタイムアウトまで待つ
  Do While timCommOut.Enabled = True: DoEvents: Loop   .....(a)
  If bgTimeOut = False Then '受信なら
  :
  :(その後の処理)

(2)OnComイベントにて
  Private Sub MSComm1_OnComm()

      Select Case MSComm1.CommEvent
       :
      Case comEvReceive
          timInterval.Enabled = True
          bgTimePass = False
          Do While bgTimePass = False: DoEvents: Loop  .....(b)

          sBuf = MSComm1.Input
          sgCommGet = sgCommGet & sBuf
          :
          :(処理)
          timCommOut.Enabled = False
          bgTimeOut = False
      End Select

      sgCommGet = ""
  End Sub

(3)各タイマー
  Private Sub timCommOut_Timer()
      timCommOut.Enabled = False
      bgTimeOut = True
  End Sub
----------
  Private Sub timInterval_Timer()
      timInterval.Enabled = False
      bgTimePass = True
  End Sub

これをVB上で走らせると、上記(b)の DoEvents で
「実行時エラー'28':スタック領域が不足しています。」
が出て止まってしまいます。

この状態で、F5を押す(継続する)と無事に最後まで行きます。

この問題を解決する方法、もしくは他の方法が良いよ、とかあればご指導、
アドバイスをいただけると助かります。
よろしくお願いします。


T★M  2006-03-16 02:21:52  No: 130648

DoEventsを実行すると、その時点でイベントが発生している場合、そちらに処理が移ります。
comEvReceiveイベントは、Inputするまで発生し続けます。
つまり、今回の場合はMSComm1_OnCommが処理途中で何度も呼ばれ、スタック領域を食いつぶすために実行時エラー28が発生しています。

このロジックのままいくなら、受信データは内部バッファに蓄えながら(Inputして受信バッファをクリアしつつ)タイムアウトを待つ方向になるでしょうか。

ただ、普通はこのような時間で受信完了という手順は行いません。機器側の仕様に送信完了の目印(EOF等)は無いのでしょうか。普通はこの目印を1回の受信完了として処理します。


ひでらん  2006-03-16 02:57:04  No: 130649

最近MSComm使用してないので、空想のレベルですが
OnCommイベントが発生する間隔を算出する様にして
データの区切りを判断してはどうだろう?
例えば100ms以上間隔が開いたらデータ区切りとか。

ただ、T★Mさんが言われている様に普通は終了を
判断する条件があるような気がしますよ。


我龍院忠太  2006-03-16 06:27:02  No: 130650

>そのため、InBufferCountで一律に規定出来ないと思ったので、以下のように
>ある程度時間をおいてから取り込むようにしています。
なぜ?  何か勘違いがあるのでは。

>この問題を解決する方法、もしくは他の方法が良いよ、とかあればご指導、
>アドバイスをいただけると助かります。
簡単な仕様の説明が必要ですね。
たとえば機器に対してIDの送信要求を出してから応答が返るまでのタイムアウト設定
が必要とか、IDを受け取った後はただ表示するだけなのか、IDを受け取って次の
動作に移る必要があるのかなどです。


GTB  2006-03-16 19:39:17  No: 130651

早々のアドバイスありがとうございます。

皆さんのご指摘のとおり、通信仕様の問題とも言えるのですが、
機器からの応答は、テキストで数行分です。区切りは<CR>ですが、
送信完了の目印はありません。
機器やコマンドにより、この行数や文字数が変わります。

次に本プログラムの仕様ですが、返ってきた値が正しいかどうかの
判断をするものです。これは併せて機器側のハードを検査する目的
も兼ねています。

ハードがNGの場合もあるので、レスポンス完了までにタイムアウトを
設けています。その値はある程度サンプルを採った後のおおよその値
としています。

今のプログラムは受信しつつの解析・判断ではなくて、受信完了後に
まとめて処理するようにしています。
最初の文中の(1)に書いた(その後の処理)のところで、返ってきた
文字の中から、必要な部分を抜き取りその値が正しいかどうかを
判断しています。

そのため受信完了を待つためにLoopを入れていました。
今回この部分がよろしくないようなので、他の手段を含め、皆さんに
相談させていただいた次第です。
自分でも改めて考えてみますが、再度アドバイスございましたら
よろしくお願いします。


我龍院忠太  2006-03-16 20:37:42  No: 130652

>ハードがNGの場合もあるので、レスポンス完了までにタイムアウトを
>設けています。
正確には機器がID送信要求を受けて、IDを送信し終わるまでの時間ですね。
機器側で測定する訳にはいかない場合は、パソコン側から送信要求を出して
IDを受信するまでの時間にタイムアウトを設定することになります。
更に、受信データーの終わりがフォーマット的に判断不能ですから、
タイマーの設定時間内に正しいIDが送られて来るか否かの判断になります。

大まかのフローは、こんなところですか。
    1、送信要求
    2、タイムアウトのタイマースタート
    3、受信
    4、タイムアップ
    5、受信データーの検査
まあ、機器にもよりますが、数行のIDを送り返すだけなら、タイマーの時間は
500msも有れば十分と思われます。

これであれば、受信のところにタイマーはいらないと思います。
    sBuf = MSComm1.Input
    sgCommGet = sgCommGet & sBuf
これだけでOKで、時間内に送られたデーターは文字列に足しこまれます。


ひでらん  2006-03-16 23:07:15  No: 130653

実は今初めてまともにソースを見たんですが、(おぃ)
OnComm内のタイマを全部取っ払って下記の様にしたら
だめなのかな?※細かいエラー処理は手抜きしました。

タイマー設定(レスポンス用)
Do 
    レスポンスタイムアウトしたら終了
    データを1バイト以上受信したら終了
Loop
タイマー停止
if  データを1バイト以上受信  Then
    タイマー再設定(受信終了用)
    Do
        タイムアウトしたら終了
    Loop
    タイマー停止
    受信データ解析
End If


GTB  2006-03-17 02:37:20  No: 130654

ありがとうございます。

機器からの返りは数行と書きましたが、最大27行(992文字)です。
OnComm内のタイマーが無い場合は頭の20文字分くらいしか受け取れなかった
のでタイマーを入れるようにしました。

我龍院さんの書いてくださったフローを使わせてもらうと
    1、送信要求
    2、タイムアウトのタイマースタート
    3、全て受信またはタイムアップ
        (受信ならタイムアウトタイマーも止める)
    4、受信ならばデータの検査
になります。

上記3の「全て受信(1回のOnComm内で全てを受信して、
タイムアウトタイマーも止める)」に無理があるのでしょうか。

確かに皆さんのアドバイスのとおり、OnComm内の待ちTimerは無くして
タイムアウトTimerだけでやる方が、Timerコントロールも1個で済みますし
良いのかもしれません。
試してみたいと思います。


我龍院忠太  2006-03-17 03:20:37  No: 130655

>上記3の「全て受信(1回のOnComm内で全てを受信して、
>タイムアウトタイマーも止める)」に無理があるのでしょうか。
無理があります。
RThresholdを1にしてますので、受信文字が1文字以上でComイベント
が発生します。従って、取れるデーターの数はボーレートとマシンスピード、
その他のタスク等に依存して何文字受信出来るのかは不確定です。
無理やりLoopを回して受信する方法は無くも有りませんが、
そのようにしなければならない理由が見つかりません。
普通にOnCommイベントが発生して自動的にバッファから読み取る
構造にしておいた方が柔軟性が有ります。
その辺に何か勘違いされている気がしてならないのですが。


ひでらん  2006-03-17 17:27:35  No: 130656

OnComm内でループさせてしまうと、せっかくのOnComm
イベントが無意味になりません?
OnComm内でLoopさせてしまうと下記と大差ないような
気が・・・。

タイマー設定(レスポンス用)
Do 
    タイムアウトしたら終了
    If MSComm1.InBufferCount > 0 Then
        sgCommGet = sgCommGet & MSComm1.Input      
    End If
Loop
タイマー停止
If 受信 Then  ・・・・


GTB  2006-03-17 18:33:06  No: 130657

先にも書きましたが、当初全部の文字を受け取ることが出来なかった時に、
ブレークポイントで止めたら受けられたので、単純にTimerを追加してみた
のでした。
皆さんのアドバイスに従って、作り直してみます。
また、何かありましたら質問するかもしれませんが、その時はよろしく
お願いします。
ありがとうございました。m(__)m


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加