Jump to content
AndrewHoward

Reading fields with different lenghts

Recommended Posts

Hi,
I'm receiving data in this format:
[STX][0x1C][Field1][0x1C][Field2][0x1C][Field3][Field4][0x1C][ETX][LRC]
Which is the best way to read the fields between [STX] & [ETX] without using a loop?
Field1, Field2, Field3 & Field4 length are 2,6,3,5 respectively.

I'm using delphi & TComPort.
Thank you.

Share this post


Link to post

Unless you are processing data at very high speeds or using a very slow platform, which is unlikely with a COM port, a loop is the ideal simple solution to your problem. Just process one character at a time. 

 

Angus

 

Edited by Angus Robertson

Share this post


Link to post

If the fields are at known, fixed positions then you can read them directly. If not then you need to parse the data. 

Edited by David Heffernan
  • Like 1

Share this post


Link to post
On 10/30/2020 at 5:36 PM, AndrewHoward said:

Hi,
I'm receiving data in this format:
[STX][0x1C][Field1][0x1C][Field2][0x1C][Field3][Field4][0x1C][ETX][LRC]
Which is the best way to read the fields between [STX] & [ETX] without using a loop?
Field1, Field2, Field3 & Field4 length are 2,6,3,5 respectively.

I'm using delphi & TComPort.
Thank you.

I would use TStringList because it gives you plenty of features already implemented, like get value by index.

I would do something like this:

 

// Parse input string into TStringList
procedure ParseInput(const aInput: string; var aData: TStringList);
var
  i, vStartOfValue: integer;
begin 
  // loop aInput and add each value between [...] to aData
  for i := 1 to aInput.Length do
  begin
    if aInput[i] = cStart then
      vStartOfValue := i+1
    else if aInput[i] = cEnd then
    begin
      aData.Add(Copy(aInput, vStartOfValue, i - vStartOfValue));
    end;
  end;
end;

Then you can get field after 'STX' like this:

 

// use GetValueAfter(Data, 'STX', 2) for 2nd field after 'STX'
function GetValueAfter(aData: TStringList; const aAfter: string; aIndex: integer): string;
begin
  Result := aData[aData.IndexOf(aAfter) + aIndex];
end;

And here is example:

 

procedure TForm2.Button1Click(Sender: TObject);
var vData: TStringList;
begin
  vData := TStringList.Create;
  try
    ParseInput('[STX][0x1C][Field1][0x1C][Field2][0x1C][Field3][Field4][0x1C][ETX][LRC]', vData);

    // Get 2nd field after 'STX'
    Memo2.Lines.Add(GetValueAfter(vData, 'STX', 2));

  finally
    vData.Free;
  end;
end;

 

Edited by Mike Torrettinni

Share this post


Link to post
31 minutes ago, Attila Kovacs said:

@Mike Torrettinni Woww. Even if the presented protocol is a binary one?

Oh, good thinking! Well if it's binary then I guess he can ignore the suggestion. Right?

Edited by Mike Torrettinni

Share this post


Link to post
On 10/31/2020 at 12:36 AM, AndrewHoward said:

[STX][0x1C][Field1][0x1C][Field2][0x1C][Field3][Field4][0x1C][ETX][LRC]
Which is the best way to read the fields between [STX] & [ETX] without using a loop?

f1 := ReadField;

f2 := ReadField;

f3 := ReadField;

f4 := ReadField;

Share this post


Link to post

Hi,

 

Showing some code always helps us give accurate answers.
Anyway....

 

If you can read Sizeof(TMyData) in one peace, you could use a record like

 

type

   TMyData = record

     bSTX : Byte;

     bSep1: byte;

     Field1: Array[0..1] of byte;

     bSep2 : Byte;

     Field2 : Array[0..5] of byte;

     bSep3: Byte;

     Field3 : Array[0..2] of byte.

     Field4 : Array[0..4] of byte;

     bsep4 : Byte;

     bETX : Byte;

     bLRC : byte;

  end;

 

var

  MyData : TMyData;

begin

  fillchar( MyData, Sizeof(TMyData) , 0 );

  move( DataFromCOM[0], MyData[0], Sizeof(TMyData) );

 // Use the data

  MyData.Field1;

  MyData.Field2;

 

end;

 

Always check if you are receiving the expected number of bytes.

Check the values of STX, ETX and the separators to see if at least those are Ok.
 

If this routine will be called "a lot", I suggest creating MyData as a class field, to be use as long as the class is not destroyed.

 

 

 

Edited by Clément
  • Like 2

Share this post


Link to post

packed record

fillchar( MyData, Sizeof(TMyData) , 0 );

MyData := Default(TMyData);

 

Edited by Kryvich

Share this post


Link to post
2 hours ago, Clément said:

fillchar( MyData, Sizeof(TMyData) , 0 );

  move( DataFromCOM[0], MyData[0], Sizeof(TMyData) );

Beyond the compilation error which is just a typo, pointless to zeroise the record and then write over those zeros. 

  • Like 1

Share this post


Link to post

it's not necessary to move the data anywhere, just cast the memory to the record

 

type

  TMyData = record
    bSTX: Byte;
    bSep1: Byte;

    Field1: Word;
    bSep2: Byte;

    Field2: Array [0 .. 5] of Byte;
    bSep3: Byte;

    Field3: Array [0 .. 2] of Byte;
    Field4: Array [0 .. 4] of Byte;

    bsep4: Byte;
    bETX: Byte;
    bLRC: Byte;
  end;

  PMyData = ^TMyData;

var
  DataFromCOM: TBytes;


begin
  SetLength(DataFromCOM, SizeOf(TMyData));
  DataFromCOM[2] := 122;
  WriteLn(PMyData(DataFromCOM).Field1.ToString);
  ReadLn;

end.

 

Edited by Attila Kovacs
  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×