【问题标题】:TimeOutException and SerialPort in Delphi PrismDelphi Prism 中的 TimeOutException 和 SerialPort
【发布时间】:2011-08-23 18:43:23
【问题描述】:

我需要我的程序在每次 SerialPort 读取超时时捕获 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 并采取适当的措施。

【问题讨论】:

    标签: exception exception-handling timeout serial-port delphi-prism


    【解决方案1】:

    您似乎将串行端口用作表单上的组件,但在后台线程中进行读/写?

    或者,据我所知,您在后台线程中写入,然后在其他一些随机线程(调用您做出反应的事件的线程)上读取。

    这是一个问题,因为后台线程(内部)想要更新串行端口“控制”,后台线程不允许这样做。问题也可能是等待读取的线程被正在无限循环中写入的另一个线程中断,从而导致 I/O 异常。这里涉及到一些猜测。

    第一枪: 您必须动态创建串行端口(即不将其放在您的表单上,而是通过代码实例化和配置它)以防止这种情况发生,或者(但强烈不鼓励)将System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls 设置为 false。

    第二枪: 另一方面,我强烈建议明确确保只有一个线程在使用串行端口。不是在一个线程中写入并从另一个线程中读取。在一个线程中完成与此串行 I/O 相关的所有事情。读或写,但不要尝试从不同的线程同时进行。

    【讨论】:

    • @Sebastian,SerialPort 是在 Winform 类中动态创建的,而不是作为控件放置在表单上。我相信我只有一个线程直接处理 SerialPort 通信。
    • 我建议在 Read 行之前调试和检查线程 ID、每次发送时的线程 ID 以及发生异常的线程 ID。我真的很想知道是否一切都是一样的。
    • @Sebastian,你是正确的,因为线程每隔一段时间或在循环数之后切换它的 ID,但只有一个线程。这是因为方法 MySerialData 是 dxCommWin 窗口窗体的一部分吗?
    • @Sebastian,我在调试线程时看到了更多有趣的东西。似乎在每个串行通信周期之后都会旋转或创建一个新线程,但它们要么处于睡眠状态,要么处于等待或加入状态。
    • 那你真的有线程问题。您应该确保所有内容都在同一个线程中运行,或者如前所述,将 CheckForIllegalCrossThreadCalls 设置为 false,但这对于应用程序来说是全局的,可能会导致其他问题。
    【解决方案2】:

    而不是: SerialPort1.Read(RXMsg,0,5);

    Delphi 有没有串行函数返回接收到的等待读取的字符数?

    例如(可能是糟糕的伪代码):

    while (Not Timeout)
    {
        if (serialport1.characterswaiting)
        {
            SerialPort1.Read(RXMsg,0,5);
        }
    }
    

    【讨论】:

    • 这是 Delphi Prism,因此是纯 .Net。
    【解决方案3】:

    我认为问题在于我在自己的线程或用户定义的线程中写入串行端口并从另一个串行端口读取。我认为收到的事件数据是程序主线程的一部分。

    正如 Sebastian 所指出的,从同一个线程写入和读取应该可以解决我的串行通信问题。确实,它似乎必须解决我的串行通信,尽管它不到 100%。这是一个时间问题,因为我的程序依赖于固定的时间延迟。

    步骤:在我的线程中,我写入串行端口并等待某个时间从串行端口读取响应。这似乎大大改善了通信,但现在我不会等待 datareceived 事件在它在输入缓冲区中看到某些内容时触发。

    如果我的想法或推理有误,请纠正我。

    【讨论】:

      猜你喜欢
      • 2011-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多