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.