Due programmi che condividono uModbusRTU e uGauge: - Console/PlanciaConsole: plancia nautica descritta da file XML, con modalita' plancia e modalita' configurazione. Comandi, spie, selettori, strumenti e allarmi sonori; il bus gira in un thread suo perche' la finestra non si fermi mai. - ProjectPlancia: il programma di prova piu' vecchio, usato per collaudare i canali. Le plance sono in Console/*.xml, la documentazione in Console/LEGGIMI.md. Esclusi dal versionamento i compilati (.exe, .dcu), i file dell'IDE e un audio da 17 MB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
129 lines
3.6 KiB
ObjectPascal
129 lines
3.6 KiB
ObjectPascal
program dumpframe;
|
|
|
|
{
|
|
Manda una singola richiesta Modbus e stampa i byte grezzi della risposta,
|
|
senza interpretarli. Serve quando una lettura fallisce e non si sa se il
|
|
problema sia nel modulo, nel cablaggio o nel codice che decodifica.
|
|
|
|
Uso: dumpframe [porta] [baud] [slave] [funzione] [start] [quantita]
|
|
Es.: dumpframe COM7 9600 1 2 0 8
|
|
}
|
|
|
|
{$APPTYPE CONSOLE}
|
|
|
|
uses
|
|
Winapi.Windows, System.SysUtils;
|
|
|
|
function CRC16(const ABuf: array of Byte; ALen: Integer): Word;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
Result := $FFFF;
|
|
for I := 0 to ALen - 1 do
|
|
begin
|
|
Result := Result xor ABuf[I];
|
|
for J := 0 to 7 do
|
|
if (Result and 1) <> 0 then
|
|
Result := (Result shr 1) xor $A001
|
|
else
|
|
Result := Result shr 1;
|
|
end;
|
|
end;
|
|
|
|
function Hex(const ABuf: TBytes; ALen: Integer): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := '';
|
|
for I := 0 to ALen - 1 do
|
|
Result := Result + IntToHex(ABuf[I], 2) + ' ';
|
|
end;
|
|
|
|
var
|
|
Port: string;
|
|
Baud, Slave, Func, Start, Qty: Integer;
|
|
H: THandle;
|
|
DCB: TDCB;
|
|
TO_: TCommTimeouts;
|
|
Req: array[0..7] of Byte;
|
|
C: Word;
|
|
Buf: TBytes;
|
|
Written, Got: DWORD;
|
|
Expected, I: Integer;
|
|
|
|
begin
|
|
Port := 'COM7'; Baud := 9600; Slave := 1; Func := 2; Start := 0; Qty := 8;
|
|
if ParamCount >= 1 then Port := ParamStr(1);
|
|
if ParamCount >= 2 then Baud := StrToIntDef(ParamStr(2), 9600);
|
|
if ParamCount >= 3 then Slave := StrToIntDef(ParamStr(3), 1);
|
|
if ParamCount >= 4 then Func := StrToIntDef(ParamStr(4), 2);
|
|
if ParamCount >= 5 then Start := StrToIntDef(ParamStr(5), 0);
|
|
if ParamCount >= 6 then Qty := StrToIntDef(ParamStr(6), 8);
|
|
|
|
H := CreateFile(PChar('\\.\' + Port), GENERIC_READ or GENERIC_WRITE, 0, nil,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if H = THandle(-1) then
|
|
begin
|
|
Writeln(Format('Impossibile aprire %s (errore %d)', [Port, GetLastError]));
|
|
Readln; Halt(1);
|
|
end;
|
|
|
|
try
|
|
FillChar(DCB, SizeOf(DCB), 0);
|
|
DCB.DCBlength := SizeOf(DCB);
|
|
GetCommState(H, DCB);
|
|
DCB.BaudRate := Baud;
|
|
DCB.ByteSize := 8;
|
|
DCB.Parity := NOPARITY;
|
|
DCB.StopBits := ONESTOPBIT;
|
|
DCB.Flags := 0;
|
|
SetCommState(H, DCB);
|
|
|
|
TO_.ReadIntervalTimeout := 50;
|
|
TO_.ReadTotalTimeoutMultiplier := 10;
|
|
TO_.ReadTotalTimeoutConstant := 500;
|
|
TO_.WriteTotalTimeoutMultiplier := 10;
|
|
TO_.WriteTotalTimeoutConstant := 500;
|
|
SetCommTimeouts(H, TO_);
|
|
PurgeComm(H, PURGE_RXCLEAR or PURGE_TXCLEAR);
|
|
|
|
Req[0] := Slave;
|
|
Req[1] := Func;
|
|
Req[2] := Hi(Word(Start)); Req[3] := Lo(Word(Start));
|
|
Req[4] := Hi(Word(Qty)); Req[5] := Lo(Word(Qty));
|
|
C := CRC16(Req, 6);
|
|
Req[6] := Lo(C); Req[7] := Hi(C);
|
|
|
|
Write ('RICHIESTA (8 byte): ');
|
|
for I := 0 to 7 do Write(IntToHex(Req[I], 2), ' ');
|
|
Writeln;
|
|
|
|
WriteFile(H, Req[0], 8, Written, nil);
|
|
|
|
// Legge molto piu' del necessario: se arriva un eco della richiesta o
|
|
// byte di troppo si devono vedere, non nascondere troncando.
|
|
Expected := 64;
|
|
SetLength(Buf, Expected);
|
|
Sleep(150); // lascia il tempo allo slave di rispondere per intero
|
|
ReadFile(H, Buf[0], Expected, Got, nil);
|
|
|
|
Writeln(Format('RISPOSTA (%d byte): ', [Got]) + Hex(Buf, Got));
|
|
|
|
if Got >= 4 then
|
|
begin
|
|
C := CRC16(Buf, Got - 2);
|
|
Writeln(Format('CRC calcolata sui primi %d byte: %s %s - nel frame: %s %s -> %s',
|
|
[Got - 2, IntToHex(Lo(C), 2), IntToHex(Hi(C), 2),
|
|
IntToHex(Buf[Got - 2], 2), IntToHex(Buf[Got - 1], 2),
|
|
BoolToStr((Lo(C) = Buf[Got - 2]) and (Hi(C) = Buf[Got - 1]), True)]));
|
|
end;
|
|
|
|
finally
|
|
CloseHandle(H);
|
|
end;
|
|
|
|
Writeln;
|
|
Write('Premi INVIO per chiudere.');
|
|
Readln;
|
|
end.
|