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

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

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// Header for SMI++ Runtime Library SMIRTL.DLL                                //
// See http://smi.web.cern.ch                                                 //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20201123 - 1st release                                                     //
// 20210122 - Update to v56r1                                                 //
// 20230906 - Modified for FPC (A.K.) & update to v58r1                       //
////////////////////////////////////////////////////////////////////////////////

unit _crw_smirtl; // SMI++ runtime library header, see http://smi.web.cern.ch

{$I _crw_sysdef}

{$IFDEF FPC}{$mode Delphi}{$ENDIF}{$H+}

 // Define the SMI_STATIC parameter to use static DLL linking.
 // When using static  linking, EXE file will depend from DLL.
 // When using dynamic linking - call LoadSmiLibrary on start,
 // and use ValidSmiLibrary to check library is really loaded.
 // If ValidSmiLibrary return false, any call of SMI functions
 // will raise general violation exception (nil address call).

{-$DEFINE SMI_STATIC}

interface

uses
 _crw_sharemem, // NB: THIS UNIT MUST BE FIRST !!!
 {$IFDEF UNIX} cthreads, dl, {$ENDIF}
 {$IFDEF WINDOWS} windows, {$ENDIF}
 sysutils, classes,
 _crw_alloc, _crw_dim;

 //
 // parameters.h
 //
const
  MAXRECL           = 81;
  MAXNAMESIZE       = 33;
  MAXOBJECTLINES    = 5000;
  MAXCLASSLINES     = 100;
  MAXSTATES         = 100;
  MAXATRIBUTES      = 10;
  MAXSUBOBJECTS     = 50;
  MAXACTIONS        = 100;
  MAXINS            = 100;
  MAXPARA           = 5;
  MAXWHENS          = 20;

 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //
 // smirtl.h
 //
 // SMIRTL implements the routines used by a proxy in order to connect,
 //        receive commands and transmit state changes to its associated object in a SM.
 //
 //
 // int smi_attach(obj_name, command_handler)
 // Parameters:
 //     char *obj_name              The name of the associated object to connect to. In the form <domain_name>::<object_name>
 //     void (*command_handler)()   The routine to be called when a command arrives from the SM, handle_command is called with no parameters.
 // Description:
 //     smi_attach should be called by a proxy at startup in order to connect to its associated object in the SM.
 // Return:
 //     0/1 if object not/attached.
 //
 //
 // void smi_volatile()
 // Parameters:
 //     none
 // Description:
 //     Smi_volatile() makes a proxy live only while its associated SMI Domain is active, i.e., a proxy can call
 //     smi_volatile() (normally at start-up) in order to die whenever the SMI domain the proxy is attached to dies.
 //
 //
 // int smi_set_state(state)
 // Parameters:
 //     char *state         The state of the proxy.
 // Description:
 //     Set the initial or new state of the proxy by sending it to its associated object in the SM.
 //     An smi_set_state should be done at start-up in order to communicate the proxy's initial state.
 // Return:
 //     1.
 //
 //
 // smi_terminate_action(state)
 // Parameters:
 //     char *state         The state of the proxy.
 // Description:
 //     Set the new state of the proxy after executing an action, by sending it to its associated object in the SM.
 // Return:
 //     1.
 //
 //
 // int smi_get_action(action, n_params)
 // Parameters:
 //     char *action        The last action received
 //     int *n_params       Number of parameters with this action
 // Description:
 //     When inside the comman_handler the user can use this routine to get the action name and nr. of parameters.
 // Return:
 //     0/1 if action is not/valid.
 //
 //
 // int smi_get_command(cmnd)
 // Parameters:
 //     char *cmnd          The command just received
 //     int *size           The command size (length)
 // Description:
 //     When inside the comman_handler the user can use this routine to get the command, the command is composed
 //     of the action name and any parameters in the form action/par0=val0/par1=val1… .
 // Return:
 //     0/1 if command not/valid.
 //
 //
 // int smi_test_action(action)
 // Parameters:
 //     char *action        Action name to compare with the received action.
 // Description:
 //     Test if the action received is "action". The function returns 1 if it is, 0 if not.
 // Return:
 //     0/1 if action not/match.
 //
 //
 // int smi_get_next_par(param, type, size)
 // Parameters:
 //     char *param         The name of the parameter
 //     int *type           The type of the parameter (STRING, INTEGER, FLOAT)
 //     int *size           The size of the parameter
 // Description:
 //     When inside the command handler this routine can be used to retrieve the parameters present on a received command.
 //     The function will return 0 when all parameters have been read, otherwise it returns the parameter name,
 //     the parameter type and the size necessary to store the parameter.
 // Return:
 //     0/1 if parameter not/retrieved.
 //
 //
 // int smi_get_par_value(param, value)
 // Parameters:
 //     char *param         The name of the parameter
 //     void *value         The parameter value
 // Description:
 //     When inside the command handler this routine can be used to retrieve the parameter value for the parameter
 //     returned by smi_get_next_par. The value argument will be filled with either an integer, a double float or
 //     a string (the size was specified in the previous routine) depending on the parameter type.
 //     This function can also be used directly if the user knows the parameter name, type and size.
 //     The function will return 0 if the specified parameter does not exist.
 // Return:
 //     0/1 if parameter not/retrieved.
 //
 //
 // int smi_set_par(par, value, type)
 // Parameters:
 //     char *par           The parameter name.
 //     void *value         The value of the parameter (passed by reference).
 //     int type            The parameter type: (STRING, INTEGER or FLOAT).
 // Description:
 //     Smi_set_par should be used in order to set object parameters for associated objects.
 //     If an associated objects has parameters, smi_set_par should be called once for each parameter before the first smi_set_state. 
 //     Further parameter setting can be done by the proxy at any time but they will only take effect at the next smi_set_state.
 // Return:
 //     0/1 if parameter was not/set.
 //
 //
 // void smi_register_termination(exit_handler, context)
 // Parameters:
 //     int (*exit_handler)(void *, int *)    
 //     void *context
 // Description:
 //     Internal use.
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

const SMIXX_VERSION                 = 5801;                             // Version of SMI++
const MAX_NAME                      = 132;                              // Max. size of name
const SMI_STRING                    = 0;                                // String  parameter
const SMI_INTEGER                   = 1;                                // Integer parameter
const SMI_FLOAT                     = 2;                                // Float   parameter
type  PAR_TYPES                     = SMI_STRING .. SMI_FLOAT;          // Parameter types
type  smi_cmnd_handler_t            = procedure(); cdecl;               // Command handler
type  smi_exit_handler_t            = function(context:Pointer; var ExitCode:Integer):Integer; cdecl; // Exit handler

{$IFDEF SMI_STATIC}

function  smi_attach(obj_name:PChar; command_handler: smi_cmnd_handler_t): Integer; cdecl;
procedure smi_volatile(); cdecl;
function  smi_set_state(state:PChar): Integer; cdecl;
function  smi_get_state(state:PChar; max_len:Integer): Integer; cdecl;
function  smi_terminate_action(state:PChar): Integer; cdecl;
function  smi_get_action(cmnd:PChar; var n_pars:Integer): Integer; cdecl;
function  smi_get_command(cmnd:PChar; var size:Integer): Integer; cdecl;
function  smi_test_action(cmnd:PChar): Integer; cdecl;
function  smi_get_next_par(param:PChar; var typ:Integer; var size:Integer): Integer; cdecl;
function  smi_get_par_value(param:PChar; value:Pointer): Integer; cdecl;
function  smi_set_par(param:PChar; value:Pointer; typ:Integer): Integer; cdecl;
procedure smi_register_termination(exit_handler:smi_exit_handler_t; context:Pointer); cdecl;

{$ELSE  SMI_STATIC}

var smi_attach: function(obj_name:PChar; command_handler: smi_cmnd_handler_t): Integer; cdecl;
var smi_volatile: procedure(); cdecl;
var smi_set_state: function(state:PChar): Integer; cdecl;
var smi_get_state: function(state:PChar; max_len:Integer): Integer; cdecl;
var smi_terminate_action: function(state:PChar): Integer; cdecl;
var smi_get_action: function(cmnd:PChar; var n_pars:Integer): Integer; cdecl;
var smi_get_command: function(cmnd:PChar; var size:Integer): Integer; cdecl;
var smi_test_action: function(cmnd:PChar): Integer; cdecl;
var smi_get_next_par: function(param:PChar; var typ:Integer; var size:Integer): Integer; cdecl;
var smi_get_par_value: function(param:PChar; value:Pointer): Integer; cdecl;
var smi_set_par: function(param:PChar; value:Pointer; typ:Integer): Integer; cdecl;
var smi_register_termination: procedure(exit_handler:smi_exit_handler_t; context:Pointer); cdecl;

{$ENDIF SMI_STATIC}

 //////////////////////////////
 // Service to load SMI library
 //////////////////////////////
function  ValidSmiRtlLibrary:Boolean; inline; // Check status
function  LoadSmiRtlLibrary(const FileName:String=''):Boolean;
procedure FreeSmiRtlLibrary; // Do not use this call directly
function  SmiRtlLibraryName:String; // smi, smirtl.dll, libsmi.so

type // For exceptions on SmiRtl load.
 ESmiRtlLibrary = class(Exception);

var // Internal SMI data, please don't change
 SmiRtlLibrary : record    // SMI library data
  Valid     : Boolean;     // Library status
  libHandle : TLibHandle;  // Library handle
  numLoaded : Integer;     // Num functions loaded
  numFailed : Integer;     // Num functions failed
  Report    : ShortString; // Report result on Load
 end = ( Valid:false; libHandle:0; numLoaded:0; numFailed:0; Report:'' );

implementation

const // Expected number of functions loaded
 ExpectedNumLoaded = 12;

{$IFDEF SMI_STATIC}

const // The Name of SMI library
 SmiRtlDll = {$IFDEF WINDOWS} 'smirtl.dll' {$ELSE} 'smi' {$ENDIF};

function  smi_attach;               external SmiRtlDll name 'smi_attach';
procedure smi_volatile;             external SmiRtlDll name 'smi_volatile';
function  smi_set_state;            external SmiRtlDll name 'smi_set_state';
function  smi_get_state;            external SmiRtlDll name 'smi_get_state';
function  smi_terminate_action;     external SmiRtlDll name 'smi_terminate_action';
function  smi_get_action;           external SmiRtlDll name 'smi_get_action';
function  smi_get_command;          external SmiRtlDll name 'smi_get_command';
function  smi_test_action;          external SmiRtlDll name 'smi_test_action';
function  smi_get_next_par;         external SmiRtlDll name 'smi_get_next_par';
function  smi_get_par_value;        external SmiRtlDll name 'smi_get_par_value';
function  smi_set_par;              external SmiRtlDll name 'smi_set_par';
procedure smi_register_termination; external SmiRtlDll name 'smi_register_termination';

function LoadSmiRtlLibrary(const FileName:String=''):Boolean;
var libName:String;
begin
 SmiRtlLibrary.Valid:=true;
 SmiRtlLibrary.libHandle:=0;
 SmiRtlLibrary.numLoaded:=ExpectedNumLoaded;
 SmiRtlLibrary.numFailed:=0;
 libName:=Trim(FileName);
 if (libName='') then libName:=SmiRtlDll;
 SmiRtlLibrary.Report:=libName+': Success';
 Result:=SmiRtlLibrary.Valid;
end;

{$ELSE  SMI_STATIC}

const // The Name of SMI library
 SmiRtlDll = {$IFDEF WINDOWS} 'smirtl.dll' {$ELSE} 'libsmi.so' {$ENDIF};

function LoadSmiRtlLibrary(const FileName:String=''):Boolean;
var lib:TLibHandle; numOk,numEr:Integer; libName:String;
 function DoGetProcAddress(ProcName:String):Pointer;
 begin
  if (lib=0)
  then Result:=nil
  else Result:=System.GetProcAddress(lib,ProcName);
  if (Result<>nil) then inc(numOk) else inc(numEr);
 end;
begin
 if not LoadDimLibrary then begin
  Result:=false; // SMI depends on DIM
  Exit;
 end;
 if SmiRtlLibrary.Valid then begin
  Result:=true; // Already done
  Exit;
 end;
 numOk:=0; numEr:=0;
 libName:=Trim(FileName);
 if (libName='') then libName:=SmiRtlDll;
 lib:=SafeLoadLibrary(libName);
 smi_attach               := DoGetProcAddress( 'smi_attach' );
 smi_volatile             := DoGetProcAddress( 'smi_volatile' );
 smi_set_state            := DoGetProcAddress( 'smi_set_state' );
 smi_get_state            := DoGetProcAddress( 'smi_get_state' );
 smi_terminate_action     := DoGetProcAddress( 'smi_terminate_action' );
 smi_get_action           := DoGetProcAddress( 'smi_get_action' );
 smi_get_command          := DoGetProcAddress( 'smi_get_command' );
 smi_test_action          := DoGetProcAddress( 'smi_test_action' );
 smi_get_next_par         := DoGetProcAddress( 'smi_get_next_par' );
 smi_get_par_value        := DoGetProcAddress( 'smi_get_par_value' );
 smi_set_par              := DoGetProcAddress( 'smi_set_par' );
 smi_register_termination := DoGetProcAddress( 'smi_register_termination' );
 SmiRtlLibrary.Valid:=(numOk=ExpectedNumLoaded) and (numEr=0);
 SmiRtlLibrary.libHandle:=lib;
 SmiRtlLibrary.numLoaded:=numOk;
 SmiRtlLibrary.numFailed:=numEr;
 if SmiRtlLibrary.Valid
 then SmiRtlLibrary.Report:=libName+': Success'
 else SmiRtlLibrary.Report:=libName+': Failure';
 {$IFDEF UNIX}
 if lib=0 then SmiRtlLibrary.Report:=StrPas(dlerror);
 {$ENDIF UNIX}
 Result:=SmiRtlLibrary.Valid;
end;

{$ENDIF SMI_STATIC}

procedure FreeSmiRtlLibrary;
begin
 if (SmiRtlLibrary.libHandle<>0) then begin
  if SmiRtlLibrary.Valid then begin
  end;
  FreeLibrary(SmiRtlLibrary.libHandle);
 end;
 SmiRtlLibrary.Valid:=false;
 SmiRtlLibrary.libHandle:=0;
 SmiRtlLibrary.numLoaded:=0;
 SmiRtlLibrary.numFailed:=0;
 SmiRtlLibrary.Report:='';
end;

function  ValidSmiRtlLibrary:Boolean;
begin
 Result:=SmiRtlLibrary.Valid;
end;

function SmiRtlLibraryName:String;
begin
 Result:=SmiRtlDll;
end;

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_smirtl;
begin
 LoadSmiRtlLibrary;
end;

procedure Free_crw_smirtl;
begin
 // FreeSmiRtlLibrary;
end;

initialization

 Init_crw_smirtl;

finalization

 Free_crw_smirtl;

end.

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

