2

在阅读了关于 stackoverflow 的这个非常有趣的主题之后 -->如何在循环中发送更多数据之前等待 COM 端口接收事件

我遇到了很多问题,我尝试了很多解决方案......不幸的是,没有任何效果!

许多串行端口库是事件驱动的,我很难理解它们。

我尝试过使用 Asyncpro、Synaser 和 TComport。

有没有可能有这样的功能:

SerialSendandReply(tx string here,timeout) 返回 rx 字符串,如果超时则发送错误字符串

来自设备的响应是毫秒级的阻塞方式会更好吗?

像这样:

Dosomething here
showmessage(SerialSendandReply('test',100 )); //100 ms timeout
dosomething else

使用此代码

TForm1 = class(TForm)
 ...
private
    IOEvent           : THandle; // used for IO events
    IORx              : string;
    Comport           : TapdComport;
...


procedure TForm1.ComportTriggerAvail(CP: TObject; Count: Word);

var i       : integer;

begin
 for i:=1 to Count do
  IORx:=IORx+Comport.GetChar;
 SetEvent(IOEvent);
end;

function TForm1.SerialSAWR(tx : string; TimeOut : integer) : boolean;
begin
 Result := False;
 try
  IORx := ''; // your global var
  ResetEvent(IOEvent);
  Comport.PutString(tx);
  Result := WaitForSingleObject(IOEvent, TimeOut) = WAIT_OBJECT_0;
 except
  on E : Exception do
   // dosomething with exception
 end;
end;

// constructor part
IOEvent := CreateEvent(nil, True, False, nil);
// destructor part
 if IOEvent <> 0 then
  CloseHandle(IOEvent);

然后我试图调用这个函数:

if SerialSAWR('test'; 5000) then showmessage(IORx);

发送效果很好,但不返回字符串中的任何内容。

有什么建议吗?

非常感谢你!

问候,劳伦特

4

3 回答 3

1

我使用 TComPort 并创建了以下例程来执行您的要求。TComPort 监视器在其监视线程中接收到字符,并且我的例程在不调用 Application.ProcessMessages 的情况下等待字符。它可能不是最优雅的代码,但可以正常工作。

function TArtTComPort.SerialPort_AwaitChars(AMinLength: integer;
      ATerminator: char; AQtyAfterTerm: integer; ARaise: boolean): string;
    var
      fDueBy : TDateTime;

      function IsEndOfReplyOrTimeout( var AStr : string ) : boolean;
      var
       I : integer;
      begin
        Result := False;
        If ATerminator <> #0 then
          begin
          I := Length( AStr ) - AQtyAfterTerm;
          If I > 0 then
            Result := AStr[I] = ATerminator;
          end;
        If not Result then
          Result := Length(AStr) >= AMinLength;


        // Un-comment this next line to disable the timeout.
        //Exit;

        If not Result then
          begin
          Result := Now > fDueBy;
          If Result then
            If ARaise then
              raise EArtTComPort.Create( 'Serial port reply timeout' )
            else
              AStr := '';
          end;
      end;

    var
      Events : TComEvents;
      iCount : integer;
      S : string;
    begin
      Assert( AMinLength > 0, 'Invalid minimum length' );

      If not FComPort.Connected then
        begin
        Result := '';
        Exit;
        end;

      fDueBy := Now + (FTimeoutMS * TDMSec );

      Result := '';


      Repeat

          // Setup events to wait for:
          Events := [evRxChar, evTxEmpty, evRxFlag, evRing, evBreak,
                   evCTS, evDSR, evError, evRLSD, evRx80Full];

          // Wait until at least one event happens.
          FComPort.WaitForEvent(
            Events,
            FStopEvent.Handle,
            FTimeOutMS);

          If Events = [] then // timeout
            begin
            If ARaise then
              raise EArtTComPort.Create( 'Serial port reply timeout' )
            end
           else
            begin
            If evRxChar in Events then
              begin
              iCount := FComport.InputCount;
              FComPort.ReadStr( S, iCount );
              Result := Result + S;
              end;
            end;

      until IsEndOfReplyOrTimeout( Result );


    end;
于 2012-12-14T08:40:19.893 回答
1

我切换到 nrComm Lib (v9.31)... 使用非常简单并且得到很好的支持。

唯一的缺点是它不是免费和开源的......但它是值得的!

它也是线程安全的,这也很好:)。

非常感谢大家的回复!

于 2012-12-15T15:30:42.150 回答
0

您正在尝试从主线程执行异步 I/O。这永远不会与 GUI 配合得很好。

执行复杂的异步 I/O 更适合在单独的线程中。我有一个阻塞串行通信包(我认为 Synaser 也有阻塞模式)和这样的功能:

function TransmitReceive(const msg: AnsiString; var reply: AnsiString; 
  timeOut: Integer): Boolean;

将复杂的代码逻辑放在 athread.execute中,并用事件信号触发逻辑的开始。

PostMessage例如,通过调用将结果等传达给主线程。

于 2012-12-13T22:45:55.760 回答