//Использование в коммерческих целях запрещено.
//Наказание - неминуемый кряк и распространение по всему инет.
//Business application is forbidden.
//Punishment - unavoidable crack and propagation on everything inet.
unit ComPort;

interface
uses Windows,SysUtils, Classes,D2XXUnit;

var
 iComNum      : integer = 2; // номер COM порта
 iOldComOpen  : integer = 0;
 iComBaud     : integer = 115200; // Скорость COM порта
 iOldComBaud  : integer = 0;
 hCom         : THandle=INVALID_HANDLE_VALUE;
 ComTimeouts  : TCommTimeouts;
 txLen, rxLen : Dword;
 FlgOvrlp     : boolean = True;
 COMwr : OVERLAPPED;
 COMrd : OVERLAPPED;
 COMst : COMSTAT;
 DCB          : TDCB;
 ComListStr:TStrings;
// port:integer;

{ Флаги в DCB :
0      DWORD fBinary: 1;          // binary mode, no EOF check
1      DWORD fParity: 1;          // enable parity checking
2      DWORD fOutxCtsFlow:1;      // CTS output flow control
3      DWORD fOutxDsrFlow:1;      // DSR output flow control
4..5   DWORD fDtrControl:2;       // DTR flow control type
6      DWORD fDsrSensitivity:1;   // DSR sensitivity
7      DWORD fTXContinueOnXoff:1; // XOFF continues Tx
8      DWORD fOutX: 1;        // XON/XOFF out flow control
9      DWORD fInX: 1;         // XON/XOFF in flow control
10     DWORD fErrorChar: 1;   // enable error replacement
11     DWORD fNull: 1;        // enable null stripping
12..13 DWORD fRtsControl:2;   // RTS flow control
14     DWORD fAbortOnError:1; // abort reads/writes on error
15..31 DWORD fDummy2:17;      // reserved}

function OpenCom(Mode:boolean) : boolean;
procedure CloseCom;
function GetComDCB : boolean;
function SetComDCB : boolean;
function GetComTimeouts : boolean;
function SetComTimeouts : boolean;
function SetComRxTimeouts(NewIntervalTimeout,NewTimeoutMultiplier,NewTimeoutConstant:dword) : boolean;
function ReadCom(Buf:Pointer; BufLen:Dword) : boolean;
function WriteCom(Buf:Pointer; BufLen:Dword) : boolean;
function EscapeComFunction(dwFunc:Dword) : boolean;
function GetComModemStatus(lpModemStat: Pointer):boolean;
function GetComStat : boolean;
function PurgeCom(mode:Dword): boolean;
function WriteComStr(S: String): boolean;
function ChangeComSpeed(Baud:integer) : boolean;
procedure ComList();
Function CheckCom():boolean;
procedure UFSList();

implementation

procedure UFSList();
var
i:integer;
s:string;
begin
    GetFTDeviceCount;
    if FT_Device_Count<1 then exit;
    for i:=0 to FT_Device_Count-1 do begin
        if FT_Open(i,@FT_Handle)=FT_OK then begin
        FT_Close(FT_Handle);
        GetFTDeviceDescription(i);
        s:= FT_Device_String;
        if Pos('UFS',s)<>0 then begin
        GetFTDeviceSerialNo(i);
        s:=s+' S/N '+ FT_Device_String;
        ComListStr.Add(s);
        end;
        end;
        end;
end;

procedure ComList();
var
i:integer;
begin
    ComListStr.Clear;
    for i:=1 to 255 do begin
        iComNum := i;
        if CheckCom = true then begin
        ComListStr.Add('COM'+IntToStr(i));
        end;
    end;
end;

Function CheckCom():boolean;
Var
hPort:THandle;//дескриптор порта
Begin {InitPort}
Result:=false;
hPort := CreateFile(PChar('\\?\COM'+IntToStr(iComNum)),
                        GENERIC_READ or GENERIC_WRITE,
                        FILE_SHARE_READ or FILE_SHARE_WRITE,
                        nil, OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL, 0);
If hPort = INVALID_HANDLE_VALUE Then Exit;
        CloseHandle(hPort);
Result:=true;
end;

function OpenCom(Mode:boolean) : boolean;
var
dw : dword;
begin
 result:=FALSE;
  if hCom<>INVALID_HANDLE_VALUE then CloseCom;
  if FlgOvrlp then dw:=FILE_FLAG_OVERLAPPED
  else dw:=FILE_ATTRIBUTE_NORMAL;
  hCom:=CreateFile(PChar('\\?\COM'+IntToStr(iComNum)),GENERIC_READ or GENERIC_WRITE,0,nil,OPEN_EXISTING,dw,0);
  if hCom<>INVALID_HANDLE_VALUE then begin
   SetupComm(hCom,$20000,$20000); //8192, 8192); // Rd , Wr
   if GetComDCB then begin
    if Mode then DCB.Flags := (DCB.Flags and $ffff8000) or $0001
    else DCB.Flags := (DCB.Flags and $ffff8000) or $0011;   //$1011;
    DCB.ByteSize := 8;
//    DCB.Parity := 0;
    DCB.StopBits := 0;
    DCB.BaudRate := iComBaud;
    if SetComDCB then begin
     ComTimeouts.ReadIntervalTimeout:=20; //0
     ComTimeouts.ReadTotalTimeoutMultiplier:=1; //0
     ComTimeouts.ReadTotalTimeoutConstant:=200; //200
     ComTimeouts.WriteTotalTimeoutMultiplier:=0;//MAXDWORD;
     ComTimeouts.WriteTotalTimeoutConstant:=0;//MAXDWORD;
     if SetComTimeouts then begin
      iOldComOpen:=iComNum;
      iOldComBaud:=iComBaud;
      if Mode then begin
       sleep(75);
       EscapeComFunction(SETRTS);
       sleep(25);
       EscapeComFunction(SETDTR);
      end
      else begin
       sleep(75);
       EscapeComFunction(SETRTS);
      end;
//      sleep(300);
//      PurgeCom(PURGE_TXCLEAR or PURGE_RXCLEAR);
      result := TRUE;
     end;
    end;
   end;
  end;
end;

procedure CloseCom;
var
x: THandle;
begin
 if hCom<>INVALID_HANDLE_VALUE then begin
  x:=hCom;
  hCom:=INVALID_HANDLE_VALUE;
  CloseHandle(x);
 end;
// hCom:=INVALID_HANDLE_VALUE;
 iOldComOpen:=0;
end;

function GetComDCB : boolean;
begin
 result := FALSE;
 if hCom<>INVALID_HANDLE_VALUE then
  result := GetCommState(hCom,DCB);
end;

function SetComDCB : boolean;
begin
 result:=FALSE;
 if hCom<>INVALID_HANDLE_VALUE then
  result := SetCommState(hCom,DCB);
end;

function ChangeComSpeed(Baud:integer) : boolean;
var
save_baud : integer;
begin
 result:=FALSE;
 save_baud:=dcb.BaudRate;
 if hCom<>INVALID_HANDLE_VALUE then begin
//  dcb.BaudRate:=Baud;
  if GetComDCB then begin
   if dcb.BaudRate<>Dword(Baud) then begin
    dcb.BaudRate:=Baud;
    result := SetCommState(hCom,DCB);
    EscapeComFunction(SETRTS);
    EscapeComFunction(SETDTR);
    if not result then begin
     dcb.BaudRate:=save_baud;
     SetCommState(hCom,DCB);
     EscapeComFunction(SETRTS);
     EscapeComFunction(SETDTR);
    end;
   end;
  end;
 end;
end;

function GetComTimeouts : boolean;
begin
 result:=FALSE;
 if hCom<>INVALID_HANDLE_VALUE then
  result := GetCommTimeouts(hCom,ComTimeouts);
end;

function SetComTimeouts : boolean;
begin
 result:=FALSE;
 if hCom<>INVALID_HANDLE_VALUE then
  if SetCommTimeouts(hCom,ComTimeouts) then result:=TRUE;
end;

function SetComRxTimeouts(NewIntervalTimeout,NewTimeoutMultiplier,NewTimeoutConstant:dword) : boolean;
begin
 result:=FALSE;
 if (ComTimeouts.ReadIntervalTimeout<>NewIntervalTimeout)
 or (ComTimeouts.ReadTotalTimeoutMultiplier<>NewTimeoutMultiplier)
 or (ComTimeouts.ReadTotalTimeoutConstant<>NewTimeoutConstant)
 then begin
  ComTimeouts.ReadIntervalTimeout:=NewIntervalTimeout;
  ComTimeouts.ReadTotalTimeoutMultiplier:=NewTimeoutMultiplier;
  ComTimeouts.ReadTotalTimeoutConstant:=NewTimeoutConstant;
  if hCom<>INVALID_HANDLE_VALUE then
   if SetCommTimeouts(hCom,ComTimeouts) then result:=TRUE;
 end
 else result:=TRUE;
end;

function  GetComStat : boolean;
var
  dErr : DWORD;
begin
 result:=FALSE;
 dErr := 0;
// rxLen := 0;
 if hCom<>INVALID_HANDLE_VALUE then begin
   if ClearCommError(hCom,dErr,@COMst) then begin
     // size Rx buff := COMst.cbInQue;
     Result := True;
   end;
 end;
end;

function ReadCom(Buf:Pointer; BufLen :Dword) : boolean;
var
 dErr: Dword;
begin
 result := False;
 if (hCom<>INVALID_HANDLE_VALUE)and(Buf<>Nil)and(BufLen<>0) then begin
  rxlen:=0;
  if FlgOvrlp then begin
   COMrd.hEvent:=CreateEvent(Nil,TRUE,FALSE,Nil);
   if not ReadFile(hCom,Buf^,BufLen,rxLen,@COMrd) then begin
    if not GetOverlappedResult(hCom,COMrd,rxLen,True) then begin
    end;
   end;
//   ResetEvent(COMrd.hEvent);
   CloseHandle(COMrd.hEvent);
  end
  else begin
   if not ReadFile(hCom,Buf^,BufLen,rxLen,Nil) then begin
//     ClearCommError(hCom,dErr,Nil);
//     exit;
   end;
  end;
  if rxLen = BufLen then result := True
  else ClearCommError(hCom,dErr,Nil);
 end;
end;

function WriteCom(Buf:Pointer; BufLen:Dword) : boolean;
Var
 dErr: Dword;
begin
 result:=FALSE;
 if (hCom<>INVALID_HANDLE_VALUE)and(Buf<>Nil)and(BufLen<>0) then begin
  txLen:=0;
  if FlgOvrlp then begin
   COMwr.hEvent:=CreateEvent(Nil,TRUE,FALSE,Nil);
   if not WriteFile(hCom,Buf^,BufLen,txLen,@COMwr) then begin
    if not GetOverlappedResult(hCom,COMwr,txLen,True) then begin
    end;
//     ClearCommError(hCom,dErr,Nil);
   end;
//   ResetEvent(COMwr.hEvent);
   CloseHandle(COMwr.hEvent);
  end
  else begin
   if not WriteFile(hCom,Buf^,BufLen,txLen,Nil) then begin
//     ClearCommError(hCom,dErr,Nil);
//     exit;
   end;
  end;
  if txLen = BufLen then result:=TRUE
  else ClearCommError(hCom,dErr,Nil);
 end;
end;

function WriteComStr(S: String): boolean;
begin
  Result:=WriteCom(@S[1],Length(S));
end;

function PurgeCom(mode:Dword): boolean; //function
begin
 result := PurgeComm(hCom,mode); // сбросить буфера
end;

function EscapeComFunction(dwFunc:Dword):boolean;
begin
 result:=FALSE;
 if hCom<>INVALID_HANDLE_VALUE then
  result := EscapeCommFunction(hCom,dwFunc);
end;


function GetComModemStatus(lpModemStat:Pointer):boolean;
begin
 result:=FALSE;
 if hCom<>INVALID_HANDLE_VALUE then
  result := GetCommModemStatus(hCom,LpDword(lpModemStat)^);
end;

end.
