📄 synaser.pas
字号:
function TBlockSerial.CanWrite(Timeout: integer): boolean;
var
FDSet: TFDSet;
TimeVal: PTimeVal;
TimeV: TTimeVal;
x: Integer;
begin
TimeV.tv_usec := (Timeout mod 1000) * 1000;
TimeV.tv_sec := Timeout div 1000;
TimeVal := @TimeV;
if Timeout = -1 then
TimeVal := nil;
FD_ZERO(FDSet);
FD_SET(integer(FHandle), FDSet);
x := Select(integer(FHandle) + 1, nil, @FDSet, nil, TimeVal);
SerialCheck(x);
if FLastError <> sOK then
x := 0;
Result := x > 0;
ExceptCheck;
if Result then
DoStatus(HR_CanWrite, '');
end;
{$ELSE}
function TBlockSerial.CanWrite(Timeout: integer): boolean;
var
t: ULong;
begin
Result := SendingData = 0;
if not Result then
Result := CanEvent(EV_TXEMPTY, Timeout);
if Result and (Win32Platform <> VER_PLATFORM_WIN32_NT) then
begin
t := GetTick;
while not ReadTxEmpty(FPortAddr) do
begin
if TickDelta(t, GetTick) > 255 then
Break;
Sleep(0);
end;
end;
if Result then
DoStatus(HR_CanWrite, '');
end;
{$ENDIF}
function TBlockSerial.CanReadEx(Timeout: integer): boolean;
begin
if Fbuffer <> '' then
Result := True
else
Result := CanRead(Timeout);
end;
procedure TBlockSerial.EnableRTSToggle(Value: boolean);
begin
SetSynaError(sOK);
{$IFNDEF WIN32}
FRTSToggle := Value;
if Value then
RTS:=False;
{$ELSE}
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
GetCommState;
if value then
dcb.Flags := dcb.Flags or dcb_RtsControlToggle
else
dcb.flags := dcb.flags and (not dcb_RtsControlToggle);
SetCommState;
end
else
begin
FRTSToggle := Value;
if Value then
RTS:=False;
end;
{$ENDIF}
end;
procedure TBlockSerial.Flush;
begin
{$IFNDEF WIN32}
SerialCheck(tcdrain(integer(FHandle)));
{$ELSE}
SetSynaError(sOK);
if not Flushfilebuffers(FHandle) then
SerialCheck(sErr);
{$ENDIF}
ExceptCheck;
end;
{$IFNDEF WIN32}
procedure TBlockSerial.Purge;
begin
SerialCheck(ioctl(integer(FHandle), TCFLSH, TCIOFLUSH));
FBuffer := '';
ExceptCheck;
end;
{$ELSE}
procedure TBlockSerial.Purge;
var
x: integer;
begin
SetSynaError(sOK);
x := PURGE_TXABORT or PURGE_TXCLEAR or PURGE_RXABORT or PURGE_RXCLEAR;
if not PurgeComm(FHandle, x) then
SerialCheck(sErr);
FBuffer := '';
ExceptCheck;
end;
{$ENDIF}
function TBlockSerial.ModemStatus: integer;
begin
Result := 0;
{$IFNDEF WIN32}
SerialCheck(ioctl(integer(FHandle), TIOCMGET, @Result));
{$ELSE}
SetSynaError(sOK);
if not GetCommModemStatus(FHandle, dword(Result)) then
SerialCheck(sErr);
{$ENDIF}
ExceptCheck;
FModemWord := Result;
end;
procedure TBlockSerial.SetBreak(Duration: integer);
begin
{$IFNDEF WIN32}
SerialCheck(tcsendbreak(integer(FHandle), Duration));
{$ELSE}
SetCommBreak(FHandle);
Sleep(Duration);
SetSynaError(sOK);
if not ClearCommBreak(FHandle) then
SerialCheck(sErr);
{$ENDIF}
end;
{$IFDEF WIN32}
procedure TBlockSerial.DecodeCommError(Error: DWord);
begin
if (Error and DWord(CE_FRAME)) > 1 then
FLastError := ErrFrame;
if (Error and DWord(CE_OVERRUN)) > 1 then
FLastError := ErrOverrun;
if (Error and DWord(CE_RXOVER)) > 1 then
FLastError := ErrRxOver;
if (Error and DWord(CE_RXPARITY)) > 1 then
FLastError := ErrRxParity;
if (Error and DWord(CE_TXFULL)) > 1 then
FLastError := ErrTxFull;
end;
{$ENDIF}
//HGJ
function TBlockSerial.PreTestFailing: Boolean;
begin
if not FInstanceActive then
begin
RaiseSynaError(ErrPortNotOpen);
result:= true;
Exit;
end;
Result := not TestCtrlLine;
if result then
RaiseSynaError(ErrNoDeviceAnswer)
end;
function TBlockSerial.TestCtrlLine: Boolean;
begin
result := ((not FTestDSR) or DSR) and ((not FTestCTS) or CTS);
end;
function TBlockSerial.ATCommand(value: string): string;
var
s: string;
ConvSave: Boolean;
begin
result := '';
FAtResult := False;
ConvSave := FConvertLineEnd;
try
FConvertLineEnd := True;
SendString(value + #$0D);
repeat
s := RecvString(FAtTimeout);
if s <> Value then
result := result + s + CRLF;
if s = 'OK' then
begin
FAtResult := True;
break;
end;
if s = 'ERROR' then
break;
until FLastError <> sOK;
finally
FConvertLineEnd := Convsave;
end;
end;
function TBlockSerial.ATConnect(value: string): string;
var
s: string;
ConvSave: Boolean;
begin
result := '';
FAtResult := False;
ConvSave := FConvertLineEnd;
try
FConvertLineEnd := True;
SendString(value + #$0D);
repeat
s := RecvString(90 * FAtTimeout);
if s <> Value then
result := result + s + CRLF;
if s = 'NO CARRIER' then
break;
if s = 'ERROR' then
break;
if s = 'BUSY' then
break;
if s = 'NO DIALTONE' then
break;
if Pos('CONNECT', s) = 1 then
begin
FAtResult := True;
break;
end;
until FLastError <> sOK;
finally
FConvertLineEnd := Convsave;
end;
end;
function TBlockSerial.SerialCheck(SerialResult: integer): integer;
begin
if SerialResult = integer(INVALID_HANDLE_VALUE) then
{$IFDEF WIN32}
result := GetLastError
{$ELSE}
{$IFNDEF FPC}
result := GetLastError
{$ELSE}
result := __errno_location^
{$ENDIF}
{$ENDIF}
else
result := sOK;
FLastError := result;
FLastErrorDesc := GetErrorDesc(FLastError);
end;
procedure TBlockSerial.ExceptCheck;
var
e: ESynaSerError;
s: string;
begin
if FRaiseExcept and (FLastError <> sOK) then
begin
s := GetErrorDesc(FLastError);
e := ESynaSerError.CreateFmt('Communication error %d: %s', [FLastError, s]);
e.ErrorCode := FLastError;
e.ErrorMessage := s;
raise e;
end;
end;
procedure TBlockSerial.SetSynaError(ErrNumber: integer);
begin
FLastError := ErrNumber;
FLastErrorDesc := GetErrorDesc(FLastError);
end;
procedure TBlockSerial.RaiseSynaError(ErrNumber: integer);
begin
SetSynaError(ErrNumber);
ExceptCheck;
end;
procedure TBlockSerial.DoStatus(Reason: THookSerialReason; const Value: string);
begin
if assigned(OnStatus) then
OnStatus(Self, Reason, Value);
end;
{======================================================================}
class function TBlockSerial.GetErrorDesc(ErrorCode: integer): string;
begin
Result:= '';
case ErrorCode of
sOK: Result := 'OK';
ErrAlreadyOwned: Result := 'Port owned by other process';{HGJ}
ErrAlreadyInUse: Result := 'Instance already in use'; {HGJ}
ErrWrongParameter: Result := 'Wrong paramter at call'; {HGJ}
ErrPortNotOpen: Result := 'Instance not yet connected'; {HGJ}
ErrNoDeviceAnswer: Result := 'No device answer detected'; {HGJ}
ErrMaxBuffer: Result := 'Maximal buffer length exceeded';
ErrTimeout: Result := 'Timeout during operation';
ErrNotRead: Result := 'Reading of data failed';
ErrFrame: Result := 'Receive framing error';
ErrOverrun: Result := 'Receive Overrun Error';
ErrRxOver: Result := 'Receive Queue overflow';
ErrRxParity: Result := 'Receive Parity Error';
ErrTxFull: Result := 'Tranceive Queue is full';
end;
if Result = '' then
begin
Result := SysErrorMessage(ErrorCode);
end;
end;
{---------- cpom Comport Ownership Manager Routines -------------
by Hans-Georg Joepgen of Stuttgart, Germany.
Copyright (c) 2002, by Hans-Georg Joepgen
Stefan Krauss of Stuttgart, Germany, contributed literature and Internet
research results, invaluable advice and excellent answers to the Comport
Ownership Manager.
}
{$IFDEF LINUX}
function TBlockSerial.LockfileName: String;
var
s: string;
begin
s := SeparateRight(FDevice, '/dev/');
result := LockfileDirectory + '/LCK..' + s;
end;
procedure TBlockSerial.CreateLockfile(PidNr: integer);
var
f: TextFile;
s: string;
begin
// Create content for file
s := IntToStr(PidNr);
while length(s) < 10 do
s := ' ' + s;
// Create file
try
AssignFile(f, LockfileName);
try
Rewrite(f);
writeln(f, s);
finally
CloseFile(f);
end;
// Allow all users to enjoy the benefits of cpom
s := 'chmod a+rw ' + LockfileName;
{$IFNDEF FPC}
Libc.system(pchar(s));
{$ELSE}
Libc.__system(pchar(s));
{$ENDIF}
except
// not raise exception, if you not have write permission for lock.
on Exception do
;
end;
end;
function TBlockSerial.ReadLockfile: integer;
{Returns PID from Lockfile. Lockfile must exist.}
var
f: TextFile;
s: string;
begin
AssignFile(f, LockfileName);
Reset(f);
try
readln(f, s);
finally
CloseFile(f);
end;
Result := StrToIntDef(s, -1)
end;
function TBlockSerial.cpomComportAccessible: boolean;
var
MyPid: integer;
Filename: string;
begin
Filename := LockfileName;
MyPid := Libc.getpid;
// Make sure, the Lock Files Directory exists. We need it.
if not DirectoryExists(LockfileDirectory) then
CreateDir(LockfileDirectory);
// Check the Lockfile
if not FileExists (Filename) then
begin // comport is not locked. Lock it for us.
CreateLockfile(MyPid);
result := true;
exit; // done.
end;
// Is port owned by orphan? Then it's time for error recovery.
if Libc.getsid(ReadLockfile) = -1 then
begin // Lockfile was left from former desaster
DeleteFile(Filename); // error recovery
CreateLockfile(MyPid);
result := true;
exit;
end;
result := false // Sorry, port is owned by living PID and locked
end;
procedure TBlockSerial.cpomReleaseComport;
begin
DeleteFile(LockfileName);
end;
{$ENDIF}
{----------------------------------------------------------------}
{$IFDEF WIN32}
function GetSerialPortNames: string;
var
reg: TRegistry;
l, v: TStringList;
n: integer;
begin
l := TStringList.Create;
v := TStringList.Create;
reg := TRegistry.Create;
try
{$IFNDEF VER100}
reg.Access := KEY_READ;
{$ENDIF}
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey('\HARDWARE\DEVICEMAP\SERIALCOMM', false);
reg.GetValueNames(l);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -