How to wait for COM port receive event before sending more data in a loop

吃可爱长大的小学妹 提交于 2019-12-19 11:35:49

问题


I'm working on a small component for writing and reading AT Commands using an old Sony Ericsson phone. Sending and writing to/from the phone is no problem at all, however I would like to be able to pause my SendATCmd function and wait for the COM Port component to notify me with a Notification Event, and then resume the SendATCmd function again.

Scenario: I want to get the count of SMS messages in the phone. Normally I'd just tell the phone: Hey, how many SMS messages do you have? and the phone would reply in the notification event. Thats all good.

But what I really want to do is something like

if SendATCmd('CountSMS')>0 then
  for 0 to SMSCount do
    AddSMSToList;

The code for SendATCmd looks like this:

function TSE_Z1010.SendATCmd(Cmd: string): TATResult;
begin
  fCOMPort.PutString(Cmd); //Sending AT command

  //Here is where I would like to pause this function
  //wait for the fCOMPort to notify me when data is available
  //and then resume this function again.

  result:=fTMPATResult;

end;

I've tried using a while-loop, pause, etc etc, but nothing's worked except for one thing, and that's when I put a ShowMessage where the pause should be. I don't know how ShowMessage works internally but it seems that it doesn't halt the program like while-loop and pause do.

====================

Fixed it.

All I had to do was to add Forms in the uses clause, and then I added while fTMPATResult.Full=false do Application.ProcessMessages; in the part where I wanted to pause the procedure.

"fTMPATResult" is the variable where the incoming COM Port data is stored, globally within the component.


回答1:


While AsyncPro does have some solutions for this (ontriggerdata), they are event based and make code difficult to read/understand.

here is SendAndWaitForResponse with AsyncPro (like Remy suggested):

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);

Best solution is to create a thread with the comport so your GUI won't be blocked. I have several applications in production with Asyncpro this way and it works like a charm...




回答2:


Any time you need to call Application.ProcessMessages() manually, you need to rethink your code design. Doubly so when calling it in a loop.

I do not know how Asynch Pro works, but the Win32 API has a WaitCommEvent() function that does what you are asking for. You call that function to ask the serial port for notification of the desired event(s) and then you can use either WaitForOverlappedResult() or WaitForSingleObject() to wait for those events to actually occur, depending on whether the serial port is operating in overlapped mode or not. No message processing is needed. I would be surprised if Asynch Pro does not somehow expose that functionality.



来源:https://stackoverflow.com/questions/9050045/how-to-wait-for-com-port-receive-event-before-sending-more-data-in-a-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!