////////////////////////////////////////////////////////////////////////////////
// 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:                                                                   //
// Simple IPC server to receive string messages via TSimpleIpcServer.         //
////////////////////////////////////////////////////////////////////////////////

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

program simpleipcrecv;

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 sysutils, classes, math, forms, interfaces, simpleipc,
 _crw_alloc, _crw_cmdargs, _crw_str, _crw_fio, _crw_fifo, _crw_rtc,
 _crw_sesman, _crw_ascio, _crw_proc, _crw_az;

{$R *.res}

//
// General variables and constants.
//
const
 Terminated      : Boolean    = false; // Program should be termunated
 optVerb         : Boolean    = false; // Option --verb   = verbose
 optPoll         : Integer    = 4;     // Option --poll p = polling period, ms
 optPref         : LongString = '';    // Option --pref p = data line prefix
 ServerID        : LongString = '';    // ServerID parameter value
 IpcServer : TSimpleIpcServer = nil;   // IPC server object
//
// Print version.
//
procedure PrintVersion;
var ver:LongString;
begin
 ver:=GetFileVersionInfoAsText(ProgName); if IsEmptyStr(ver) then Exit;
 StdOut.Put:=CookieScan(ver,'ProductName')+' version '+CookieScan(ver,'ProductVersion');
end;
//
// Print help.
//
procedure PrintHelp;
var ver,bn:LongString;
begin
 ver:=GetFileVersionInfoAsText(ProgName); if IsEmptyStr(ver) then Exit;
 StdOut.Put:=CookieScan(ver,'ProductName')+' version '+CookieScan(ver,'ProductVersion');
 StdOut.Put:=CookieScan(ver,'FileDescription');
 StdOut.Put:=CookieScan(ver,'LegalCopyright');
 StdOut.Put:=CookieScan(ver,'Comments');
 bn:=ExtractBaseName(ProgName);
 StdOut.Put:='Usage:';
 StdOut.Put:=' '+bn+' [-options] ServerID';
 StdOut.Put:=' where:';
 StdOut.Put:='  -options  - short (-h) or long (--help) options';
 StdOut.Put:='  -h,--help - show help';
 StdOut.Put:='  --version - show version';
 StdOut.Put:='  --verb    - verbose output';
 StdOut.Put:='  --poll p  - polling period (p), ms';
 StdOut.Put:='  --pref p  - prefix (p) to print messages';
 StdOut.Put:='  ServerID  - mandatory IPC server identifier';
 StdOut.Put:='Exit code:';
 StdOut.Put:='  0 - succeed';
 StdOut.Put:='  1 - error';
 StdOut.Put:='Example:';
 StdOut.Put:='  '+bn+' -help';
 StdOut.Put:='  '+bn+' ipc_test';
 StdOut.Put:='  '+bn+' -poll 4 -pref @recv ipc_demo';
end;
//
// Request: @help
// Reply:   none
// Comment: Print help screen.
//
procedure DoHelp(const cmnd,args:LongString);
begin
 StdOut.Put:='Command line:';
 StdOut.Put:=' simpleipcrecv ServerID';
 StdOut.Put:='Commands:';
 StdOut.Put:=' @Help        - this help.';
 StdOut.Put:=' @Exit=n      - exit process';
end;
//
// Request: @exit
// Request: @exit=n
// Reply:   @exit=n
// Comment: Terminate program with exit code n.
//
procedure DoExit(const cmnd,args:LongString);
begin
 Terminated:=true;
 System.ExitCode:=StrToIntDef(args,0);
 StdOut.Put:=Format('%s=%d',[cmnd,System.ExitCode]);
end;
//
// Get EXE file version info.
//
function GetVersionInfo(const Name:LongString):LongString;
begin
 Result:=CookieScan(GetFileVersionInfoAsText(ProgName),Name);
end;
function DotEnding(const S:LongString):LongString;
const dot='.';
begin
 Result:=S;
 if (Result<>'') then if (StrFetch(Result,Length(Result))<>dot) then Result:=Result+dot;
end;
procedure PrintVersionInfo(const Fallback:LongString);
begin
 if not IsEmptyStr(GetVersionInfo('ProductName')) then begin
  StdOut.Put:=DotEnding(GetVersionInfo('ProductName')+' version '+GetVersionInfo('ProductVersion'));
  StdOut.Put:=DotEnding(GetVersionInfo('FileDescription'));
  StdOut.Put:=DotEnding(GetVersionInfo('LegalCopyright'));
 end else begin
  StdOut.Put:=Fallback;
 end;
end;
//
// Clean IPC.
//
procedure IpcClean;
begin
 FreeAndNil(IpcServer);
end;
//
// Application specific commands handling.
//
procedure DoSpecificCommands(const data:LongString);
begin
 if Length(data)>0 then
 try
 except
  on E:Exception do BugReport(E,nil,'DoSpecificCommands');
 end;
end;
//
// Application specific initialization.
//
procedure SpecificInitialization;
begin
 CmdArgs.ListOptVal:='-poll;--poll;-pref;--pref';
 if CmdArgs.HasOption('-verb') then optVerb:=true;
 if CmdArgs.HasOption('--verb') then optVerb:=true;
 if CmdArgs.HasOption('-verbose') then optVerb:=true;
 if CmdArgs.HasOption('--verbose') then optVerb:=true;
 if CmdArgs.HasOption('-h')
 or CmdArgs.HasOption('-help')
 or CmdArgs.HasOption('--help') then begin
  PrintHelp; Terminated:=true;
  Exit;
 end;
 if CmdArgs.HasOption('-version')
 or CmdArgs.HasOption('--version') then begin
  PrintVersion; Terminated:=true;
  Exit;
 end;
 UseRunCommandEx(True);
 SystemEchoProcedure:=StdOutEcho;
 PrintVersionInfo('Simple IPC receiver.');
 // --poll
 if CmdArgs.HasOption('-poll')
 then optPoll:=StrToIntDef(CmdArgs.GetOptionValue('-poll'),optPoll);
 if CmdArgs.HasOption('--poll')
 then optPoll:=StrToIntDef(CmdArgs.GetOptionValue('--poll'),optPoll);
 optPoll:=EnsureRange(optPoll,1,100);
 if optVerb then writeln(StdErr,'Option --poll '+IntToStr(optPoll));
 // --pref
 if CmdArgs.HasOption('-pref')
 then optPref:=TrimDef(CmdArgs.GetOptionValue('-pref'),optPref);
 if CmdArgs.HasOption('--pref')
 then optPref:=TrimDef(CmdArgs.GetOptionValue('--pref'),optPref);
 if optVerb then writeln(StdErr,'Option --pref '+optPref);
 // Get ServerID
 ServerID:=CmdArgs.GetParam(1);
 if IsEmptyStr(ServerID) then begin
  writeln(StdErr,'Error: ServerID is not specified.');
  ExitCode:=1; Terminated:=true;
  Exit;
 end else StdOut.Put:='Start SimpleIpcServer: '+ServerID;
 //
 // Register user commands coming from StdIn.
 //
 StdIn.SpecHandler:=DoSpecificCommands;
 StdIn.AddCommand('@Help',        DoHelp);
 StdIn.AddCommand('@Exit',        DoExit);
 //
 // Init IPC server
 //
 IpcServer:=TSimpleIpcServer.Create(nil);
 IpcServer.Global:=true;
 IpcServer.ServerID:=ServerID;
 IpcServer.Active:=true;
 AddExitProc(IpcClean);
end;
//
// Application specific finalization.
//
procedure SpecificFinalization;
begin
 IpcClean;
end;
//
// Application specific polling.
//
procedure SpecificPolling;
var Line:LongString;
begin
 if Assigned(IpcServer) then
 if IpcServer.Active then begin
  while IpcServer.PeekMessage(0,true) do begin
   Line:=IpcServer.StringMessage;
   if (Line='') then continue;
   if (optPref='')
   then StdOut.Put:=Line
   else StdOut.Put:=optPref+' '+Line;
  end;
 end;
 if BecameZombie(FILE_TYPE_PIPE,1000) then StdIn.Put:='@Exit';
end;
//
// Main program
//
begin
 try
  try
   SpecificInitialization;
   while not Terminated do begin
    while StdIn.Count>0 do StdIn.Process(StdIn.Get);
    SpecificPolling;
    Sleep(optPoll);
   end;
  finally
   SpecificFinalization;
  end;
 except
  on E:Exception do begin BugReport(E,nil,'Main'); ExitCode:=1; end;
 end;
 Sleep(200);
 if BecameZombie(FILE_TYPE_PIPE,0) then ExitCode:=1;
end.
//////////////
// END OF FILE
//////////////

