AndrewHoward 0 Posted October 30, 2020 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
Angus Robertson 574 Posted October 31, 2020 (edited) 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 October 31, 2020 by Angus Robertson Share this post Link to post
David Heffernan 2345 Posted October 31, 2020 (edited) If the fields are at known, fixed positions then you can read them directly. If not then you need to parse the data. Edited October 31, 2020 by David Heffernan 1 Share this post Link to post
David Schwartz 426 Posted November 1, 2020 use a series of Copy statements specifying the start col and field length Share this post Link to post
Mike Torrettinni 198 Posted November 1, 2020 (edited) 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 November 1, 2020 by Mike Torrettinni Share this post Link to post
Attila Kovacs 629 Posted November 1, 2020 @Mike Torrettinni Woww. Even if the presented protocol is a binary one? Share this post Link to post
Attila Kovacs 629 Posted November 1, 2020 @AndrewHoward cast the data to a corresponding packed record 1 Share this post Link to post
Mike Torrettinni 198 Posted November 1, 2020 (edited) 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 November 1, 2020 by Mike Torrettinni Share this post Link to post
Fr0sT.Brutal 900 Posted November 2, 2020 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
Clément 148 Posted November 2, 2020 (edited) 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 November 2, 2020 by Clément 2 Share this post Link to post
Kryvich 165 Posted November 2, 2020 (edited) packed record > fillchar( MyData, Sizeof(TMyData) , 0 ); MyData := Default(TMyData); Edited November 2, 2020 by Kryvich Share this post Link to post
David Heffernan 2345 Posted November 2, 2020 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. 1 Share this post Link to post
Attila Kovacs 629 Posted November 2, 2020 (edited) 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 November 2, 2020 by Attila Kovacs 1 Share this post Link to post