Reputation: 69
I'm trying to reading a simple string from host on Serial line RS232. The parameters of receive - transmission are the same for both:
Baudrate: 9600;
Data: 8bit;
Parity: None;
Stop: 1bit;
Flow Control: None;
I've tried to send this string:
!A243B324C213D300#
I use '!' and '#' as header and finish to be sure the string will be received at all. The problem is that I can receive the string until the 15th character (3).
I know this because if I use this code:
procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
begin
ComPort1.ReadStr(Str, Count);
memo1.Lines.Add(str);
end;
I will see
!A243B324C213D3
00#
I've tried to extend the buffer of the comport, without results.
So why I can't receive the complete string in one shot?
I found this library here and it works fine until now:
http://sourceforge.net/projects/comport/
Upvotes: 1
Views: 11173
Reputation: 51
With TComPort component comes another one: TComDataPacket. Simply connect this to TComPort, setup StartString and StopString properties and use event onPacket to get complete data.
example:
...
ComDataPacket1.Comport := Comport1;
ComDataPacket1.StartString := '!';
ComDataPacket1.StopString := '#';
ComDataPacket1.onPacket := ComDataPacket1Packet;
...
//this is real code from one of my applications where I use it for barcode reader service
procedure TDM.ComDataPacket1Packet(Sender: TObject; const Str: string);
begin
BarCodeReader.Barcode := Str;
end;
Upvotes: 4
Reputation: 19106
You cannot rely on receiving complete messages in any communications at that low level of communication. Also you cannot rely on receiving only one message at a time.
You must implement something, that will guarantee to notify only on complete messages.
To do so you have to store the incoming data until you receive the complete message (header and finish flags).
Here is a small console app with a TMessageBuffer
class, that handles the incoming data and complete messages
program so_22436319;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils;
type
TMessageBuffer = class
private
FBuffer : string;
FMsgPart : Boolean;
procedure DoHandleMessage( const CompleteMessage : string );
public
procedure AddData( const Data : string );
end;
procedure Test;
var
LMsgBuffer : TMessageBuffer;
begin
LMsgBuffer := TMessageBuffer.Create;
try
// receive complete message
LMsgBuffer.AddData( '!A243B324C213D300#' );
// receive 2 complete message in one go
LMsgBuffer.AddData( '!A243B324C213D300#!A243B324C213D300#' );
// receive parts of the message
LMsgBuffer.AddData( '!A243B324' );
LMsgBuffer.AddData( 'C213D300#!A243' );
LMsgBuffer.AddData( 'B324C213D300#!A' );
LMsgBuffer.AddData( '243B324C2' );
LMsgBuffer.AddData( '13D300#' );
finally
LMsgBuffer.Free;
end;
end;
{ TMessageBuffer }
procedure TMessageBuffer.AddData( const Data : string );
var
LIdx : Integer;
LChar : Char;
begin
for LIdx := 1 to Length( Data ) do
begin
LChar := Data[LIdx];
if FMsgPart then
if LChar = '#' then
begin
DoHandleMessage( FBuffer );
FMsgPart := False;
FBuffer := '';
end
else
begin
FBuffer := FBuffer + LChar
end
else if LChar = '!' then
begin
FMsgPart := True;
end;
end;
end;
procedure TMessageBuffer.DoHandleMessage( const CompleteMessage : string );
begin
Writeln( 'MSG: ', CompleteMessage );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
The generated output is
MSG: A243B324C213D300 MSG: A243B324C213D300 MSG: A243B324C213D300 MSG: A243B324C213D300 MSG: A243B324C213D300 MSG: A243B324C213D300
The class removes the header and finish char, because this is part of the transportation protocol and therefore not needed any more. But you can still add it if you like.
Upvotes: 3