//This file was generated by the LASAL2 CodeGenerator  -- 
//Please, do not edit this file (it might be overwritten by the next generator run)
//{{LSL_DECLARATION

(*!
<Class
	Name               = "HwcLogging"
	Revision           = "1.0"
	GUID               = "{242765B4-6E7B-4655-8F6E-16157F0D8FE3}"
	RealtimeTask       = "false"
	CyclicTask         = "false"
	BackgroundTask     = "false"
	Sigmatek           = "true"
	OSInterface        = "false"
	HighPriority       = "false"
	Automatic          = "false"
	UpdateMode         = "Prescan"
	IconPath           = ".\Class\Logging.ico"
	SharedCommandTable = "true"
	Objectsize         = "(726,120)"
	Comment            = "No object of this class is necessary to use the logging functions. Only if a temporary override of the spam protection is necessary, an object has to be created.&#13;&#10;If there is no object of class HwControl in the project, the function HWC_LogInit() has to be called before any logging functions, which are:&#13;&#10;HWC_LogError(pThis, pMsg)&#13;&#10;HWC_LogValue1(pThis, pMsg, v1)&#13;&#10;HWC_LogValue2(pThis, pMsg, v1, v2)&#13;&#10;HWC_LogValue3(pThis, pMsg, v1, v2, v3)">
	<Channels>
		<Server Name="ClassSvr" GUID="{82271A2D-0B98-4986-8BA8-485D9A7406E7}" Visualized="false" Initialize="false" WriteProtected="true" Retentive="false"/>
		<Server Name="OverrideSpamProtection" GUID="{68760BB2-D90D-4660-9A6F-24665156B225}" Visualized="false" Initialize="false" WriteProtected="false" Retentive="false"/>
		<Client Name="To_SigCLib" Required="false" Internal="false"/>
	</Channels>
	<Dependencies>
		<Files>
			<File Path=".\Class\HwControl\HwControlLogging.h" Include="false"/>
		</Files>
	</Dependencies>
	<RevDoku>
		<Owner Company="Sigmatek" Author="RamAnd"/>
		<Dokumentation Revision="1.0" Date="08.11.2019" Author="RamAnd" Company="Sigmatek" Description="First library version"/>
	</RevDoku>
</Class>
*)
HwcLogging : CLASS
  //Servers:
	ClassSvr 	: SvrChCmd_DINT;
	OverrideSpamProtection 	: SvrCh_UDINT;
  //Clients:
	To_SigCLib 	: CltChCmd_SigCLib;
  //Variables:
  //Functions:
	
	FUNCTION VIRTUAL GLOBAL OverrideSpamProtection::Write
		VAR_INPUT
			input (EAX) 	: UDINT;
		END_VAR
		VAR_OUTPUT
			result (EAX) 	: UDINT;
		END_VAR;
  //Tables:
	FUNCTION @STD
		VAR_OUTPUT
			ret_code	: CONFSTATES;
		END_VAR;
	FUNCTION GLOBAL TAB @CT_;
END_CLASS;

#pragma usingLtd SigCLib


//}}LSL_DECLARATION


FUNCTION GLOBAL TAB HwcLogging::@CT_
0$UINT,
2#0100000000000000$UINT, //TY_HWCLOGGING
1$UINT, 0$UINT, (SIZEOF(::HwcLogging))$UINT, 
2$UINT, 1$UINT, 0$UINT, 
TO_UDINT(3078259981), "HwcLogging", //Class
TO_UDINT(0), 0, 0$UINT, 0$UINT, //Baseclass
//Servers:
(::HwcLogging.ClassSvr.pMeth)$UINT, _CH_CMD$UINT, 2#0000000000000000$UINT, TO_UDINT(619352855), "ClassSvr", 
(::HwcLogging.OverrideSpamProtection.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(3598353539), "OverrideSpamProtection", 
//Clients:
(::HwcLogging.To_SigCLib.pCh)$UINT, _CH_CLT_OBJ$UINT, 2#0000000000000000$UINT, TO_UDINT(1202264232), "To_SigCLib", TO_UDINT(77440046), "SigCLib", 0$UINT, 42$UINT, 
END_FUNCTION


#define USER_CNT_HwcLogging 0

TYPE
	_LSL_STD_VMETH	: STRUCT
			CmdTable	: CMDMETH;
			UserFcts	: ARRAY[0..USER_CNT_HwcLogging] OF ^Void;
	END_STRUCT;
END_TYPE

FUNCTION HwcLogging::@STD
	VAR_OUTPUT
		ret_code	: CONFSTATES;
	END_VAR
	VAR
		vmt	: _LSL_STD_VMETH;
	END_VAR

	//Command Methods
	InitCmdTable (nCmd := nSTDCMD + USER_CNT_HwcLogging, pCmd := #vmt.CmdTable);
	ClassSvr.pMeth		:= StoreCmd (pCmd := #vmt.CmdTable, SHARED);

	IF ClassSvr.pMeth THEN
		ret_code	:= C_OK;
	ELSE
		ret_code	:= C_OUTOF_NEAR;
		RETURN;
	END_IF;
	OverrideSpamProtection.pMeth			:= StoreMethod( #M_RD_DIRECT(), #OverrideSpamProtection::Write() );
	IF OverrideSpamProtection.pMeth THEN
		ret_code	:= C_OK;
	ELSE
		ret_code	:= C_OUTOF_NEAR;
		RETURN;
	END_IF;

END_FUNCTION

//{{LSL_IMPLEMENTATION

VAR_PRIVATE
  p_KernelLog               : ^DINT;
  aHexDigitTable            : ARRAY [0..15] OF CHAR;
  pHexDigitTable            : ^CHAR;
  // spam protection is handled completely via functions and private variables so it can also work without a HwControl object
#ifdef HWCL_ENABLE_SPAM_PROTECTION
  sSpamFilter               : t_HWCSpamFilter;
  b_OverrideSpamProtection  : BOOL;
#endif
END_VAR


FUNCTION GLOBAL _HWCL_LogInit
#ifndef HWCL_DISABLE_FILE_LOGGING
  VAR
    p_SSR			: ^LSL_SSR;
  END_VAR

  // Class GetCILInterface is not used here on purpose => we want to be able to log also if there is no instance of the HwcLogging or HwControl available
  // and we also want to log in the constructor (VaranManager has some messages there). The class GetCILInterface doesn't work without an instance,
  // because it resets all pointers in the constructor and without an object there is no constructor call and all interface pointers appear valid (not NIL) but aren't.

  //get pointer for logging function
  if ( OS_CILGET( "SSR", #p_SSR ) ) then
#else
#pragma message("Info: Logging of the hardware classes is disabled! Logs are only visible in the Debugger Trace!")
#endif
    p_KernelLog := NIL;
#ifndef HWCL_DISABLE_FILE_LOGGING
  else
    //Log function is available
    p_KernelLog := ( p_SSR^.KernelLog )$^DINT;
  end_if;
#endif

  _strcpy(dest:=#aHexDigitTable[0], src:="0123456789ABCDEF");
  pHexDigitTable := #aHexDigitTable[0];

#ifdef HWCL_ENABLE_SPAM_PROTECTION
  b_OverrideSpamProtection := FALSE;
  _memset(dest:=#sSpamFilter, usByte:=0, cntr:=sizeof(sSpamFilter));
#endif

END_FUNCTION


FUNCTION _HWCL_DtoH
  VAR_INPUT
    udNumber		: UDINT;
    pString			: ^CHAR;
  END_VAR
  VAR
    ValueFound  : BOOL;
    i           : INT;
    Value       : HSINT;
  END_VAR

  // get string from UDINT value without leading zeroes (_itoa puts leading zeroes if the value is not 0 itself)

  // set value to "0" and terminate it (with the cast)
  pString^$UINT := '0';

  if udNumber then
    ValueFound := FALSE;

    // go through all the digits and start on the left
    i := 7;
    repeat   
      // get value of current digit
      Value := (udNumber SHR i*4)$HSINT AND 16#F;
      
      // if we found a value or already found one before (skip leading zeroes)
      if ValueFound | Value then
        ValueFound  := TRUE;

        pString^    := (pHexDigitTable + Value)^;

        pString     += sizeof(CHAR);
      end_if;
      
      i -= 1;
    until i = -1 end_repeat;
    
    // terminate the string
    pString^ := 0;
  end_if;

END_FUNCTION


FUNCTION GLOBAL _HWCL_LimitObjName
  VAR_INPUT
    pObjName : ^CHAR;
  END_VAR
  VAR
    StringLen : UDINT;
  END_VAR
  
  if pObjName then
    StringLen := _strlen(src:=pObjName);
    
    // if the size is bigger
    if StringLen > HWCL_MAX_OBJECT_NAME_SIZE then
      // reduce it
      
      // ~ shows that some characters are missing here
      (pObjName + HWCL_MAX_OBJECT_NAME_SIZE - HWCL_OBJECT_NAME_END_CHARS - 1)^ := '~';
      
      // take the last x characters and move them inside our boundaries (also take the termination)
      _memcpy(ptr1:=pObjName + HWCL_MAX_OBJECT_NAME_SIZE - HWCL_OBJECT_NAME_END_CHARS, ptr2:=pObjName + StringLen - HWCL_OBJECT_NAME_END_CHARS, cntr:=HWCL_OBJECT_NAME_END_CHARS + 1);
    end_if;
  end_if;

END_FUNCTION


FUNCTION _HWCL_MyTrace
  VAR_INPUT
    pFormat 	: ^CHAR;
    pTxt0 	: ^CHAR;
    pTxt1 	: ^CHAR;
    pTxt2 	: ^CHAR;
  END_VAR
  VAR
    nLen      : UDINT;
    pChr      : ^CHAR;
    nTxt      : UDINT;
    nRpl      : UDINT;
    pTxt      : ^CHAR;
    pText     : ^CHAR;
    FormText  : ARRAY [0 .. 255] OF CHAR;
#ifdef HWCL_ENABLE_SPAM_PROTECTION
    InfoText  : ARRAY [0 .. 255] OF CHAR;
    NumStr    : ARRAY [0..9] OF CHAR;
#endif
  END_VAR

  // this method is a workaround for the TRACE message limitation problem

  pText := #FormText[0];

  _memset(dest:=#FormText[0], usByte:=0, cntr:=256); // to keep the zero termination

  _strncpy(pText, pFormat, 250);
  nLen  := _strlen(pText);
  pChr  := pText;
  
  WHILE nLen DO
    IF pChr^ = '{' THEN         // works for the first 10 replacement texts (single digit numbers)
      nTxt  := (pChr+1)^ - '0';
      CASE nTxt OF
        0:  pTxt  := pTxt0;
        1:  pTxt  := pTxt1;
        2:  pTxt  := pTxt2;
      ELSE
        pTxt  := "INV";
      END_CASE;
      nRpl  := _strlen(pTxt);
      if nLen - 3 + nRpl > 250 then
        pTxt := "LEN";  // replace the replacement text with "LEN" to see that we're out of length for this string
        nRpl := 3;
      end_if;
      _memmove(pChr + nRpl, pChr + 3, nLen);
      _memcpy(pChr, pTxt, nRpl);
      pChr  += nRpl;  // next character to parse is after the replacement text
      nLen  -= 3;     // the remaining length of the format string is decremented by 3
      if nLen > 250 then  // catch overrun of nLen
        nLen := 0;
      end_if;
    ELSE
      pChr  += 1;
      nLen  -= 1;
    END_IF;
  END_WHILE;

  TRACE(#FormText[0]);
  if ( p_KernelLog ) then
#ifdef HWCL_ENABLE_SPAM_PROTECTION

(*
  Erweiterungsmöglichkeit:
  
  Während blockiert wird die ersten x (10?) eintreffenden Nachrichten merken und zählen falls diese mehrfach blockiert wurden. 
  Nach der Blockade die Nachrichten + Anzahl der Blockierungen ausgeben. Meist werden die gleichen Nachrichten gespamt.
*)

    if sSpamFilter.bd_SpamFlags.FilterActive = FALSE then
      if ((ops.tAbsolute - sSpamFilter.aLogTimes[sSpamFilter.ud_NextTimeIndex]) < HWCL_SPAM_DETECTION_TIME) & (sSpamFilter.bd_SpamFlags.Initialized = TRUE) & (b_OverrideSpamProtection = FALSE) then
        // too many messages in the monitored time period
        sSpamFilter.bd_SpamFlags.FilterActive := TRUE;
        sSpamFilter.SkippedMessages := 1; // set it to 1, because we're already blocking the current message

        // info of the spam filter start
        _strcpy(dest:=#InfoText[0], src:="HWCL_Log: Log spamming detected! Writing to log file suspended!");
        TRACE(#InfoText[0]);
        OS_KERNELLOG0(#InfoText[0]);
      else
        // save the value
        sSpamFilter.aLogTimes[sSpamFilter.ud_NextTimeIndex] := ops.tAbsolute;
        if sSpamFilter.ud_NextTimeIndex = HWCL_SPAM_DETECTION_LIMIT-1 then
          sSpamFilter.ud_NextTimeIndex := 0;

          // remember that we have a comparison value for the next time (doesn't make sense to compare ops.tabsolute with 0 for the first x times)
          sSpamFilter.bd_SpamFlags.Initialized := TRUE;
        else
          sSpamFilter.ud_NextTimeIndex += 1;
        end_if;
#endif
        OS_KERNELLOG0(#FormText[0]);
#ifdef HWCL_ENABLE_SPAM_PROTECTION
      end_if;    
    else
      if (ops.tAbsolute - sSpamFilter.ud_LastLog) > HWCL_SPAM_BLOCK_RELEASE_TIME then

        // info of the spam filter stop
        _HWCL_DtoH(sSpamFilter.SkippedMessages, #NumStr[0]);

        _strcpy(dest:=#InfoText[0], src:="HWCL_Log: Blocked 0x");
        _strcat(dest:=#InfoText[0], src:=#NumStr[0]);        
        _strcat(dest:=#InfoText[0], src:=" log messages due to spamming! Writing to log file resumed!");
        
        TRACE(#InfoText[0]);
        OS_KERNELLOG0(#InfoText[0]);
        
        // original message
        OS_KERNELLOG0(#FormText[0]);

        // save the value
        sSpamFilter.aLogTimes[sSpamFilter.ud_NextTimeIndex] := ops.tAbsolute;
        if sSpamFilter.ud_NextTimeIndex = HWCL_SPAM_DETECTION_LIMIT-1 then
          sSpamFilter.ud_NextTimeIndex := 0;

          // remember that we have a comparison value for the next time (doesn't make sense to compare ops.tabsolute with 0 for the first x times)
          sSpamFilter.bd_SpamFlags.Initialized := TRUE;
        else
          sSpamFilter.ud_NextTimeIndex += 1;
        end_if;

        sSpamFilter.bd_SpamFlags.FilterActive := FALSE;
      else
        // count skipped messages
        sSpamFilter.SkippedMessages += 1;
      end_if;
    end_if;
    sSpamFilter.ud_LastLog := ops.tAbsolute;
#endif
  end_if;

END_FUNCTION


FUNCTION GLOBAL _HWCL_LogError
  VAR_INPUT
    pThis   : pVirtualBase;
    e_msg 	: ^CHAR;
  END_VAR
  VAR
    sz_name : ARRAY [ 0..255 ] OF CHAR;
  END_VAR

  if pThis then
    _GetObjName( pThis, #sz_name[ 0 ] );

    _HWCL_LimitObjName(#sz_name[ 0 ]);

    _HWCL_MyTrace(pFormat:="Object {0}: {1}", pTxt0:=(#sz_name[ 0 ])$^CHAR, pTxt1:= e_msg, pTxt2:=NIL);
  else
    _HWCL_MyTrace(pFormat:="Object {0}: {1}", pTxt0:="<unknown>", pTxt1:= e_msg, pTxt2:=NIL);
  end_if;

END_FUNCTION


FUNCTION GLOBAL _HWCL_LogValue
  VAR_INPUT
    pThis   : pVirtualBase;
    pString : ^CHAR;
    Value 	: UDINT;
    Value2 	: UDINT;(* := 16#FFFFFFFF *)
    Value3 	: UDINT;(* := 16#FFFFFFFF *)
  END_VAR
  VAR
    TempStr       : ARRAY [0..99] OF CHAR;
    TempStr2      : ARRAY [0..99] OF CHAR;
    TempStr3      : ARRAY [0..99] OF CHAR;
    sz_name       : ARRAY [ 0..255 ] OF CHAR;
    TmpWithObjName: ARRAY [ 0..255 ] OF CHAR;
  END_VAR

  
  if pString <> NIL then
    // object name
    if pThis then
      _GetObjName( pThis, #sz_name[ 0 ] );

      _HWCL_LimitObjName(#sz_name[ 0 ]);

      _strcpy(dest:=#TmpWithObjName[0], src:="Object ");
      _strcat(dest:=#TmpWithObjName[0], src:=#sz_name[0]);
      _strcat(dest:=#TmpWithObjName[0], src:=": ");
    else
      _strcpy(dest:=#TmpWithObjName[0], src:="Object <unknown>: ");
    end_if;
    
    // message
    _strcat(dest:=#TmpWithObjName[0], src:=pString);
    
    // the performance of _HwcDtoH has been tested and proved being faster than sigclib_htoa and sigclib_htoa_ex (the more digits, the faster _HwcDtoH is against the others)
    _HWCL_DtoH(Value, #TempStr[0]);
    _HWCL_DtoH(Value2, #TempStr2[0]);
    _HWCL_DtoH(Value3, #TempStr3[0]);

    _HWCL_MyTrace(pFormat:=#TmpWithObjName[0], pTxt0:=#TempStr[0], pTxt1:=#TempStr2[0], pTxt2:=#TempStr3[0]);

  else
    _HWCL_MyTrace(pFormat:="<invalid string>", pTxt0:=NIL, pTxt1:=NIL, pTxt2:=NIL);
  end_if;
  
END_FUNCTION


FUNCTION GLOBAL __CDECL HWC_LogPrintf
  VAR_INPUT
    pThis   : pVirtualBase;
    format : ^char;
    p0 : ^void := NIL;
    p1 : ^void := NIL;
    p2 : ^void := NIL;
    p3 : ^void := NIL;
    p4 : ^void := NIL;
    p5 : ^void := NIL;
    p6 : ^void := NIL;
    p7 : ^void := NIL;
    p8 : ^void := NIL;
    p9 : ^void := NIL;
  END_VAR
  VAR_OUTPUT
    retcode : udint;
  END_VAR
  VAR
  	str : array [0..255] of char;
  END_VAR

  retcode :=  sigclib_sprintfST(pd:=#str[0], format, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
  _HWCL_LogError(pThis:=pThis, e_msg:=#str[0]);
  
END_FUNCTION


FUNCTION VIRTUAL GLOBAL HwcLogging::OverrideSpamProtection::Write
	VAR_INPUT
		input (EAX) 	: UDINT;
	END_VAR
	VAR_OUTPUT
		result (EAX) 	: UDINT;
	END_VAR

  OverrideSpamProtection := 
#ifdef HWCL_ENABLE_SPAM_PROTECTION
  b_OverrideSpamProtection := 
#endif
  input <> 0;

	result := input;

END_FUNCTION
