Plancia configurabile su Modbus RTU
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>
This commit is contained in:
commit
4a01a2ec88
69 files changed
+11658
No files matched your search
@@ -0,0 +1,128 @@
|
||||
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.
|
||||
Reference in new issue
Block a user