0

SerialPort ReadがタイムアウトするたびにTimeOutExceptionをキャッチするプログラムが必要ですが、それができません。実際、プログラムは読み取りに行くと中断し、「スレッドの終了またはアプリケーション要求のいずれかが原因でI/O操作が中止されました」という例外をスローします。

SerialPortのインスタンス化方法は次のとおりです。

dxComm = class(System.Windows.Forms.Form)
private
protected
public
    constructor;
    serialPort1:System.IO.Ports.SerialPort;
    thr:Thread;
    method mythread;
end;

constructor DXComm;
begin
  //
  // Required for Windows Form Designer support
  //
  InitializeComponent();

  //
  // TODO: Add any constructor code after InitializeComponent call
  //
  SerialPort1 := new System.Io.Ports.SerialPort();
  thr:=nil;
end;

スレッドの作成方法は次のとおりです。

          thr:= new Thread(@mythread);
          thr.Start;

SerialPortの設定は次のとおりです。

   case TypeDXCard.SelectedIndex of

    0:
      begin
        DXProtocol := TDXProtocol.tDxTwo;
        msglen := 6;
        rmsglen := 5;
      end;
    1:
      begin
        DXProtocol := TDXProtocol.tDxExpress;
        msglen:=0;
        rmsglen:=0;
      end;

    else
      begin
        DXProtocol := TDXProtocol.tDxTwo;
        msglen := 6;
        rmsglen := 5;
      end;
  end;

  dx := ord(DXProtocol);

  if (SerialPort1 <> nil) then
  begin
      case CommPort.SelectedIndex of
        0: SerialPort1.PortName := 'COM1';
        1: SerialPort1.PortName := 'COM2';
        2: SerialPort1.portName := 'COM3';
        3: SerialPort1.PortName := 'COM4';
      end;    

       case BaudRate.SelectedIndex of
         0: SerialPort1.BaudRate := 1200;
         1: SerialPort1.BaudRate := 2400;
         2: SerialPort1.BaudRate := 4800;
         3: SerialPort1.BaudRate := 9600;
         4: SerialPort1.BaudRate := 19200;
         5: SerialPort1.BaudRate := 38400;
         6: SerialPort1.BaudRate := 57600;
         7: SerialPort1.BaudRate := 115200;
      end;

      if (EvenParity.Checked) then
        SerialPort1.Parity := System.IO.Ports.Parity.Even
      else
        SerialPort1.Parity := System.IO.Ports.Parity.None;
  end;

  with SerialPort1 do
  begin
    SerialPort1.DataBits:=8;
    SerialPort1.DtrEnable:=true;
    SerialPort1.ReadBufferSize:= 4096;
    SerialPort1.ReadTimeout:=TimeOutDelay*2;
    SerialPort1.RtsEnable:=true;
    SerialPort1.StopBits:=System.IO.Ports.StopBits.One;
    SerialPort1.WriteTimeout:=1000;
    SerialPort1.Handshake := HandShake.None;
    SerialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(@MySerialData);
  end;

これがSerialPort.Writeを処理する私のスレッドです:

method DXcomm.mythread;
var x,y:Integer;
begin
    while true do
    begin        
        Thread.Sleep(ScanTime);
        SerialPort1.RtsEnable:=true;
        SerialPort1.DiscardOutBuffer;
        SendMessage;   <---------Assembles the bytes and sends it out
        while SerialPort1.BytesToWrite>0 do;
        thread.Sleep(4);
        SerialPort1.DiscardInBuffer;
        SerialPort1.RtsEnable:=false;

        if (stopthread) then
            break;
    end;
end;

シリアルポートからバイトを読み取るためのイベントは次のとおりです。

method DXComm.MySerialData(sender: System.Object; e:SerialDataReceivedEventArgs);
begin
    if not SerialPort1.IsOpen then Exit;   

    try
        SerialPort1.Read(RXMsg,0,5); <------Here is Where my program throws that exception when I check on TimeOutException down below.

          if changeFlag then
          begin
               changeList.IncRxCnt;
               FixUpChangeList;
          end
          else
              ActiveUnit.Retreive;       
    except on ex: TimeOutException do <----This line of code fails.
    //except on ex: Exception do      <----This line of code works fine, but executes all the time instead of just only when there is an exception.
    begin
        //TimeOut Exception
        ActiveUnit.Timeout;
        SerialPort1.DiscardInBuffer;
        SerialPort1.DiscardOutBuffer;
    end;
    end;
end;

私は何が間違っているのですか?SerialPort.Read TimeOutsをキャッチし、適切なアクションを実行する必要があります。

4

3 に答える 3

1

フォームのコンポーネントとしてシリアルポートを使用しているようですが、バックグラウンドスレッドで読み取り/書き込みを行っていますか?

または、私が理解したように、バックグラウンドスレッドで書き込み、次に他のランダムなスレッド(反応するイベントを呼び出しているスレッド)で読み取ります。

バックグラウンドスレッドは(内部的に)シリアルポート「コントロール」を更新したいので、これは問題です。これは、バックグラウンドスレッドからは許可されていません。問題は、読み取りを待機しているスレッドが、無限ループで書き込みを行っている他のスレッドによって中断され、I/O例外が発生することでもある可能性があります。ここでは少し推測が必要です。

最初のショット:シリアルポートを動的に作成するか(つまり、フォームに配置せずに、コードでインスタンス化して構成する)、または(強くお勧めしませんが)System.Windows.Forms.Control.CheckForIllegalCrossThreadCallsfalseに設定する必要があります。

セカンドショット:一方、シリアルポートで動作しているスレッドは1つだけであることを明確に確認することを強くお勧めします。あるスレッドに書き込んだり、別のスレッドから読み取ったりしない。このシリアルI/Oに関連するすべてを1つのシングルスレッドで実行します。読み取りまたは書き込みを行いますが、異なるスレッドから同時に両方を実行しようとしないでください。

于 2011-08-24T05:56:12.553 に答える
0

代わりに:SerialPort1.Read(RXMsg、0,5);

Delphiには、受信して読み取られるのを待っている文字数を返すシリアル関数がありますか?

たとえば(おそらく貧弱な擬似コードで):

while (Not Timeout)
{
    if (serialport1.characterswaiting)
    {
        SerialPort1.Read(RXMsg,0,5);
    }
}
于 2011-08-24T04:41:05.407 に答える
0

問題は、自分のスレッドまたはユーザー定義のスレッドでシリアルポートに書き込み、別のスレッドでシリアルポートから読み取っているという事実にあると思います。受信したイベントデータは、プログラムのメインスレッドの一部だと思います。

セバスチャンが指摘したように、同じスレッドからの書き込みと読み取りが私のシリアル通信の問題を解決するはずだということは理にかなっています。確かに、それは100%弱ですが、私のシリアル通信を解決しなければならないようです。私のプログラムは固定時間遅延に依存しているので、これはタイミングの問題です。

手順:スレッド内で、シリアルポートに書き込み、シリアルポートからの応答を読み取るまでしばらく待ちます。これにより通信が大幅に改善されたようですが、入力バッファーに何かが見つかったら、datareceivedイベントが発生するのを待ちません。

私の考えや推論が間違っている場合は、私を訂正してください。

于 2011-08-31T14:31:14.023 に答える