////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2023 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWKIT.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// List LCARD devices.                                                        //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20240808 - Created by A.K.                                                 //
////////////////////////////////////////////////////////////////////////////////

program lsldev; // List LCARD devices

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

{$R *.res}

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 sysutils, classes, math,
 _crw_alloc, _crw_cmdargs, _crw_str, _crw_fio, _crw_lcard;

procedure StdOutEcho(const Msg:LongString);
begin
 write(StdOut,Msg);
end;

procedure StdErrEcho(const Msg:LongString);
begin
 write(StdErr,Msg);
end;

function VerInfo(key:LongString=''):LongString;
const ver:LongString='';
begin
 if (ver='') then ver:=GetFileVersionInfoAsText(ProgName);
 if (key='') then Result:=ver else Result:=CookieScan(ver,key);
end;

function DotEnding(const S:LongString):LongString;
const dot='.';
begin
 Result:=S;
 if (Result='') then Exit;
 if (StrFetch(Result,Length(Result))<>dot) then Result:=Result+dot;
end;

procedure Run_version;
begin
 if IsEmptyStr(VerInfo) then Exit;
 StdOutEcho(DotEnding(VerInfo('ProductName')+' version '+VerInfo('ProductVersion'))+EOL);
end;

procedure Run_help;
var exe:LongString;
begin
 Run_version;
 exe:=ProgBaseName;
 StdOutEcho(TrimDef(DotEnding(VerInfo('LegalCopyright')),'Copyright(c) 2021-2024 Alexey Kuryakin daqgroup@mail.ru.')+EOL);
 StdOutEcho(TrimDef(DotEnding(VerInfo('FileDescription')),exe+' - utility to list LCARD devices.')+EOL);
 StdOutEcho('Usage:'+EOL
           +' '+exe+' [-options] [parameters]'+EOL
           +'Options:'+EOL
           +' --version  - show version'+EOL
           +' -h,--help  - show help info'+EOL
           +'Parameters:'+EOL
           +' list of slot numbers or device links/names'+EOL
           +' slot numbers lays in [1 … 128] range'+EOL
           +'Examples:'+EOL
           +' '+exe+' --version     # show version'+EOL
           +' '+exe+' --help        # show help info'+EOL
           +' '+exe+'               # print list of connected LCARD devices'+EOL
           +' '+exe+' /dev/ldev0    # information on LCARD device /dev/ldev0'+EOL
           +' '+exe+' ldev0         # information on LCARD device /dev/ldev0'+EOL
           +' '+exe+' 0 1           # information on LCARD Virtual Slot 0, 1'+EOL
           );
end;

function Run_listldev:Integer;
var slot,ndev,icod,lcod:Integer; ifac:LUnknown; ldev:IDaqLDevice;
var pd:PLATA_DESCR_U2; sp:SLOT_PAR; hdev:THandle;
var lnk:LongString;
 procedure PrintRow(scod:Integer; info:LongString='');
 var line:LongString;
 begin
  if (ndev=0) then begin
   line:=Format('%-4s %-4s %-12s %-17s %-10s %-15s %-3s %-15s',
               ['#DEV','SLOT','PATHNAME','STATUS','TYPE:NAME','BOARD_NAME','REV','SERIAL_NUMBER']);
   StdOutEcho(Line+EOL);
  end;
  inc(ndev);
  line:=Format('%-4d %-4d %-12s',[ndev,slot,lnk]);
  line:=Format('%s %-17s',[line,LErrorCodeName(scod)]);
  line:=Format('%s %s',[line,info]);
  StdOutEcho(line+EOL);
 end;
 function GetBoardInfo(ldev:IDaqLDevice):LongString;
 var ec:Integer;
 begin
  Result:='';
  if Assigned(ldev) then begin
   hdev:=ldev.OpenLDevice;
   try
    if (hdev=INVALID_HANDLE_VALUE)
    then Exit(LErrorCodeName(GetLastOsError));
    sp:=Default(SLOT_PAR);
    pd:=Default(PLATA_DESCR_U2);
    ec:=ldev.GetSlotParam(sp);
    if (ec<>L_SUCCESS) then Exit(LErrorCodeName(ec));
    ec:=ldev.ReadPlataDescr(pd);
    if (ec<>L_SUCCESS) then Exit(LErrorCodeName(ec));
    Result:=Format('%-10s',[LBoardTypeName(sp)]);
    Result:=Format('%s %-15s',[Result,LPlataBrdName(sp,pd)]);
    Result:=Format('%s %-3s',[Result,LPlataRev(sp,pd)]);
    Result:=Format('%s %-15s',[Result,LPlataSerNum(sp,pd)]);
   finally
    ldev.CloseLDevice;
   end;
  end;
 end;
begin
 Result:=0;
 ndev:=0;
 ifac:=nil; ldev:=nil;
 sp:=Default(SLOT_PAR);
 pd:=Default(PLATA_DESCR_U2);
 for slot:=L_MIN_SLOT to L_MAX_SLOT do begin
  lnk:=LDeviceLink(slot);
  if not LDeviceExists(lnk) then continue;
  ifac:=LCreateInstance(slot);
  icod:=GetLastOsError;
  if (ifac=nil) then begin
   if (icod=L_ERROR_NOBOARD) then continue;
   if (icod=L_ERROR) then continue;
   PrintRow(icod);
   continue;
  end;
  lcod:=ifac.QueryInterface(IID_ILDEV,ldev);
  if (lcod<>L_SUCCESS) or (ldev=nil) then begin
   PrintRow(icod,LErrorCodeName(lcod));
   continue;
  end;
  PrintRow(lcod,GetBoardInfo(ldev));
 end;
 StdOutEcho(Format('%d LCARD device(s) found.',[ndev])+EOL);
end;

function Run_SlotInfo(slot:Integer):Integer;
var icod,lcod,ec:Integer; ifac:LUnknown; ldev:IDaqLDevice;
var pd:PLATA_DESCR_U2; sp:SLOT_PAR; hdev:THandle;
var lnk:LongString;
  procedure PrintParam(const aName,aValue:LongString);
  begin
   StdOutEcho(Format('%-20s %s',[aName+':',aValue])+EOL);
  end;
begin
 Result:=0;
 if not InRange(slot,L_MIN_SLOT,L_MAX_SLOT) then Exit;
 StdOutEcho(Format('Information on LCARD Virtual Slot[%d]:',[slot])+EOL);
 lnk:=LDeviceLink(slot); ifac:=nil; ldev:=nil;
 sp:=Default(SLOT_PAR); pd:=Default(PLATA_DESCR_U2);
 if not LDeviceExists(lnk) then begin
  StdOutEcho('Error: NOT FOUND device '+lnk+EOL+EOL);
  Exit(1);
 end;
 ifac:=LCreateInstance(slot);
 icod:=GetLastOsError;
 if (ifac=nil) then begin
  if (icod=L_ERROR) or (icod=L_ERROR_NOBOARD)
  then StdOutEcho('Error: LCARD DEVICE NOT CONNECTED'+EOL+EOL)
  else StdOutEcho('Error: LCreateInstance '+LErrorCodeName(icod)+EOL+EOL);
  Exit(1);
 end;
 lcod:=ifac.QueryInterface(IID_ILDEV,ldev);
 if (lcod<>L_SUCCESS) or (ldev=nil) then begin
  StdOutEcho('Error: QueryInterface '+LErrorCodeName(lcod)+EOL+EOL);
  Exit(1);
 end;
 if Assigned(ldev) then begin
  hdev:=ldev.OpenLDevice;
  try
   if (hdev=INVALID_HANDLE_VALUE) then begin
    StdOutEcho('Error: OpenLDevice '+LErrorCodeName(GetLastOsError)+EOL+EOL);
    Exit(1);
   end;
   sp:=Default(SLOT_PAR);
   pd:=Default(PLATA_DESCR_U2);
   ec:=ldev.GetSlotParam(sp);
   if (ec<>L_SUCCESS) then begin
    StdOutEcho('Error: GetSlotParam '+LErrorCodeName(ec)+EOL+EOL);
    Exit(1);
   end;
   ec:=ldev.ReadPlataDescr(pd);
   if (ec<>L_SUCCESS) then begin
    StdOutEcho('Error: ReadPlataDescr '+LErrorCodeName(ec)+EOL+EOL);
    Exit(1);
   end;
   PrintParam('Slot',IntToStr(slot));
   PrintParam('Device.Link',LDeviceLink(slot));
   PrintParam('Device.Path',LDevicePath(slot));
   PrintParam('Slot.Base',Format('0x%x',[sp.Base]));
   PrintParam('Slot.BaseL',IntToStr(sp.BaseL));
   PrintParam('Slot.Mem',IntToStr(sp.Mem));
   PrintParam('Slot.MemL',IntToStr(sp.MemL));
   PrintParam('Slot.BoardType', LBoardTypeName(sp));
   PrintParam('Slot.DSPType',IntToStr(sp.DSPType));
   PrintParam('Slot.Irq',IntToStr(sp.Irq));
   PrintParam('Plata.Test',LErrorCodeName(ldev.PlataTest));
   PrintParam('Plata.BrdName',LPlataBrdName(sp,pd));
   PrintParam('Plata.Rev',LPlataRev(sp,pd));
   PrintParam('Plata.SerNum',LPlataSerNum(sp,pd));
   PrintParam('Plata.Quartz',IntToStr(LPlataQuartz(sp,pd)));
   PrintParam('Plata.IsDacPresent',IntToStr(LPlataIsDacPresent(sp,pd)));
   PrintParam('Plata.CRC',IntToStr(LPlataCrc(sp,pd)));
   PrintParam('Plata.KoefADC',LPlataKoefAdcCsv(sp,pd));
   PrintParam('Plata.KoefDAC',LPlataKoefDacCsv(sp,pd));
   PrintParam('Plata.Custom',LPlataCustomCsv(sp,pd));
   PrintParam('Plata.CustomHex',LPlataCustomHex(sp,pd));
  finally
   ldev.CloseLDevice;
  end;
 end;
 StdOutEcho(EOL);
end;

function Run_lsldev:Integer;
var i,slot:Integer; par:LongString;
  procedure TryTakeSlot(var par:LongString; const dev:LongString);
  var len:Integer;
  begin
   len:=Length(dev)-1;
   if SameText(Copy(par,1,len),Copy(dev,1,len)) then Delete(par,1,len);
  end;
begin
 Result:=0;
 if CmdArgs.HasOption('-version')
 or CmdArgs.HasOption('--version') then begin
  Run_version;
  Exit;
 end;
 if CmdArgs.HasOption('-h')
 or CmdArgs.HasOption('-help')
 or CmdArgs.HasOption('--help') then begin
  Run_help;
  Exit;
 end;
 if not LKernelModulesLoad
 then raise Exception.Create('Could not load LCARD kernel modules.');
 if not LoadLCreateInstance
 then raise Exception.Create('Could not load '+LCOMP_DLL_NAME+' library.');
 if (ParamCount=0) then begin
  Result:=Run_listldev;
  Exit;
 end;
 for i:=1 to CmdArgs.NumParams do begin
  par:=Trim(CmdArgs.GetParam(i));
  TryTakeSlot(par,LDevicePath(0));
  TryTakeSlot(par,LDeviceLink(0));
  TryTakeSlot(par,ExtractFileName(LDevicePath(0)));
  TryTakeSlot(par,ExtractFileName(LDeviceLink(0)));
  if TryStrToInt(par,slot) then Run_SlotInfo(slot);
 end;
end;

begin
 try
  ExitCode:=Run_lsldev;
 except
  on E:Exception do begin
   StdErrEcho('Error: '+E.Message+EOL);
   ExitCode:=1;
  end;
 end;
end.

//////////////
// END OF FILE
//////////////

