delphi - ¿Cómo aumentar el número de datos en serie asignados a la variable?
serial-port delphi-7 (3)
Deberías hacer tu propio buffering. Eso le da más flexibilidad en cuanto a lo que delimita un mensaje. Por ejemplo, si delimita los mensajes con un retorno de carro (# 13), puede hacer:
var Buf: string;
procedure DoSomething(const Str: String);
begin
Memo.Text := Memo.Text + Str;
end;
procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var I: Integer;
begin
ComPort.ReadStr(CPort.Str, Count);
Buf := Buf + CPort.Str;
for i := 1 to Length(Buf) do
if Buf[i] = #13 then
begin
DoSomething(Copy(Buf, 1, i));
Delete(Buf, 1, i+1);
Break;
end;
end;
He descargado e instalado la biblioteca de conducta. Tengo un simulador que envía datos a través de la serie RS232 a un programa Selphi. Este es el siguiente fragmento de código.
procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
begin
ComPort.ReadStr(CPort.Str, Count);
Memo.Text := Memo.Text + CPort.Str;
end;
En cuanto a la parte de la biblioteca CPort, agregué:
var
Str: String;
here is the problem.
El ejemplo de datos que está llegando es más o menos como
$HEHDT,288.45,T*1D
$HEHDT,288.46,T*18
$HEHDT,288.47,T*1A
y así. Cada línea se envía por segundo. Entonces, con el código anterior, la nota mostrará todos estos datos como tal.
Sin embargo, si cambio el código a:
procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
begin
ComPort.ReadStr(CPort.Str, Count);
Memo.Text := Memo.Text + CPort.Str + ''haha'';
end;
Esto es lo que aparece en la nota:
$HEHDT,2haha88.45,T*haha1Dhaha
$HEHDT,2haha88.46,T*haha18haha
$HEHDT,2haha88.47,T*haha1Ahaha
El "haha" aparece después de 8 caracteres ASCII. Entonces, ¿eso significa que en la biblioteca CPort.pas, en la parte asíncrona / síncrona, solo se asignan 8 caracteres ASCII máximos a la variable Str?
¿Cómo hago para cambiar los códigos de modo que toda la cadena de datos, independientemente de su tamaño, se asigne a la variable Str en lugar de solo 8 bytes?
ACTUALIZACIÓN ** Me di cuenta de que hay esta parte de la biblioteca CPort que contiene el siguiente código. ¿Alguien puede aclararme sobre cómo editar el código? o si es el bloque correcto que he obtenido. ¡Gracias!
// split buffer in packets
procedure TComDataPacket.HandleBuffer;
procedure DiscardPacketToPos(Pos: Integer);
var
Str: string;
begin
FInPacket := True;
if Pos > 1 then
begin
Str := Copy(Buffer, 1, Pos - 1); // some discarded data
Buffer := Copy(Buffer, Pos, Length(Buffer) - Pos + 1);
DoDiscard(Str);
end;
end;
procedure FormPacket(CutSize: Integer);
var
Str: string;
begin
Str := Copy(Buffer, 1, CutSize);
Buffer := Copy(Buffer, CutSize + 1, Length(Buffer) - CutSize);
CheckIncludeStrings(Str);
DoPacket(Str);
end;
procedure StartPacket;
var
Found: Integer;
begin
// check for custom start condition
Found := -1;
DoCustomStart(Buffer, Found);
if Found > 0 then
DiscardPacketToPos(Found);
if Found = -1 then
begin
if Length(FStartString) > 0 then // start string valid
begin
Found := Pos(Upper(FStartString), Upper(Buffer));
if Found > 0 then
DiscardPacketToPos(Found);
end
else
FInPacket := True;
end;
end;
procedure EndPacket;
var
Found, CutSize, Len: Integer;
begin
// check for custom stop condition
Found := -1;
DoCustomStop(Buffer, Found);
if Found > 0 then
begin
// custom stop condition detected
CutSize := Found;
FInPacket := False;
end
else
if Found = -1 then
begin
Len := Length(Buffer);
if (FSize > 0) and (Len >= FSize) then
begin
// size stop condition detected
FInPacket := False;
CutSize := FSize;
end
else
begin
Len := Length(FStartString);
Found := Pos(Upper(FStopString),
Upper(Copy(Buffer, Len + 1, Length(Buffer) - Len)));
if Found > 0 then
begin
// stop string stop condition detected
CutSize := Found + Length(FStopString) + Len - 1;
FInPacket := False;
end;
end;
end;
if not FInPacket then
FormPacket(CutSize); // create packet
end;
Si está utilizando la biblioteca ComPort de Dejan Crnila, debería echarle un vistazo al componente TComDataPacket
, especialmente sus eventos OnPacket
y OnCustomStop
.
Actualización: Aquí hay un ejemplo de código. Suponiendo que la cadena de inicio es siempre la misma y la cadena final comienza con '', T *'' seguido de dos caracteres (quizás un código CRC). Si esa suposición es incorrecta, la lógica de parada debe ajustarse.
procedure TForm136.ComDataPacketCustomStop(Sender: TObject; const Str: string;
var Pos: Integer);
begin
Pos := System.Pos('',T*'', Str);
if (Pos > 0) then begin
Inc(Pos, 4);
if Pos <= Length(Str) then Exit;
end;
Pos := -1;
end;
procedure TForm136.FormCreate(Sender: TObject);
begin
ComDataPacket.ComPort := ComPort;
ComDataPacket.StartString := ''$HEHDT,'';
ComDataPacket.OnCustomStop := ComDataPacketCustomStop;
end;
Una solución minimalista con verificación completa de errores sería:
Actualización:
(Dado que la cadena recibida termina con una combinación CRLF y la longitud del paquete no es constante. Este es un paquete NMEA 0183)
var
finalBuf: AnsiString;
// Example packet: $HEHDT,10.17,T*28 + CRLF
// This is a NMEA 0183 protocol (Marine and GPS standard)
// Subset for reading the heading.
// HDT Heading – True
// 1 2 3
// | | |
//$--HDT,x.x,T*hh
//1) Heading Degrees, true
//2) T = True
//3) Checksum
// HE stands for: Heading – North Seeking Gyro
{- Checking packet and checksum, calculating heading }
Function ParseAndCheckNMEA_HDT(const parseS: AnsiString;
var heading: Double): Integer;
// Example packet: $HEHDT,10.17,T*28 + CRLF
var
i, p, err: Integer;
xorSum: Byte;
xorStr: AnsiString;
headingStr: AnsiString;
begin
Result := 0; // Assume ok
if (Pos(''$HEHDT'', parseS) = 1) then // Start header ok ?
begin
p := Pos(''*'', parseS);
if (p <> 0) and (Length(parseS) >= p + 2) then
// Assumes a checksum in packet
begin
xorSum := Ord(parseS[2]);
for i := 3 to p - 1 do // Calculate checksum
xorSum := xorSum xor Ord(parseS[i]);
xorStr := IntToHex(xorSum, 2);
if (UpperCase(xorStr) = Copy(parseS, p + 1, 2)) then // Checksum ok ?
begin
// Validate heading
headingStr := Copy(parseS, 8, p - 10);
Val(headingStr, heading, err);
if (err <> 0) then
Result := 4; // Not a valid float
end
else
Result := 3; // Wrong checksum
end
else
Result := 2; // No checksum
end
else
Result := 1; // Wrong header
end;
procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var
i,err: Integer;
strBuf: AnsiString;
heading: Double;
begin
ComPort.ReadStr(CPort.Str, Count);
strBuf := CPort.str;
for i := 1 to Length(strBuf) do
case strBuf[i] of
''$'' :
finalBuf := ''$''; // Start of package
#10 :
begin
if (finalBuf <> '''') and (finalBuf[1] = ''$'') then // Simple validate check
begin
SetLength( finalBuf, Length(finalBuf) - 1); // Strips CR
err := ParseAndCheckNMEA_HDT(finalBuf, heading);
if (err = 0) then
Memo.Lines.Add(finalBuf); // Add validated string
//else
// Memo.Lines.Add(''Error:'' + IntToStr(err));
end;
finalBuf := '''';
end;
else
finalBuf := finalBuf + strBuf[i];
end;
end;
Conociendo el carácter de inicio y el final del paquete, esto debería ser bastante seguro de usar. Los caracteres # 13 y # 10 (CR LF) que marcan el final del paquete, se eliminan y el paquete se comprueba para una suma de comprobación válida y se calcula el valor del encabezado resultante. La cadena validada se agrega a la nota.
Actualización 2
Para responder la pregunta directa, ¿por qué su método de recepción puede agregar su cadena ''jaja'' en el medio de la cadena de datos?
La rutina de comportamiento proporciona datos, uno o más caracteres, a su propio ritmo. No puede controlar cuándo obtendrá los datos o cuántos caracteres habrá. Usando el esquema en mi respuesta, los datos se almacenan en un búfer hasta que se entrega un paquete completo. Con el soporte de paquete de TComPort es posible hacer lo mismo.
Según sus comentarios, parece que hay varios tipos de sensores NMEA 0183 conectados al puerto serie. (Dando otras longitudes de los paquetes pero todos comenzando con $ character).
Reemplace ParseAndCheckNMEA_HDT con la siguiente función para validar las cadenas en ese caso:
Function ParseAndCheckNMEA(const parseS: AnsiString): Integer;
// Example packet: $HEHDT,10.17,T*28 + CRLF
var
i, p: Integer;
xorSum: Byte;
xorStr: AnsiString;
begin
Result := 0; // Assume ok
if (Pos(''$'', parseS) = 1) then // Start header ok ?
begin
p := Pos(''*'', parseS);
if (p <> 0) and (Length(parseS) >= p + 2) then
// Assumes a checksum in packet
begin
xorSum := Ord(parseS[2]);
for i := 3 to p - 1 do // Calculate checksum
xorSum := xorSum xor Ord(parseS[i]);
xorStr := IntToHex(xorSum, 2);
if (UpperCase(xorStr) <> Copy(parseS, p + 1, 2)) then // Checksum ok ?
Result := 3; // Wrong checksum
end
else
Result := 2; // No checksum
end
else
Result := 1; // Wrong header
end;