FUNCTION_BLOCK "FB_StateManager"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      stInSettings : Struct
         nStartState : Int := 100;   // State auf den als erstes gesprungen wird wenn xFunctoinEnable = TRUE
         nStartStateTestMode : Int;
         xRestartAtStartState : Bool;   // TRUE: Wird der Baustein wieder aktiviert wird der State der unter "Settings.nStartState" angegeben ist angesprungen. False: Alter State wird wieder angesprungen
         xActUnivTOn : Bool;
      END_STRUCT;
      xInFunctionEnabled : Bool;   // nState springt auf 0 wenn FALSE, und zurück zum Ursprung bei TRUE
      xInReset : Bool;
      xInRunUnivTOn : Bool;
      xInTimerReset : Bool;
      xInTestMode : Bool;
   END_VAR

   VAR_OUTPUT 
      nOutStateLast : Int;
      xOutStepchange : Bool;   // einen Zyklus True bei Schrittwechel. Kommt nicht beim Wechel in State = 0 aus State = 0
      tOutTimeInStep : Time;   // Zeit wie lange schon im Schritt, wird bei Stepchange zurückgesetzt
      tOutUnivTonR : Time;   // Gibt eine hochzählende Zeit aus die z.B. in Schrittketten verwendet werden kann
   END_VAR

   VAR_IN_OUT 
      nInOutState : Int;
   END_VAR

   VAR RETAIN
      nStateBeforeError : Int := 100;
      nStateLast : Int := 100;
      arHistory : Array[0..50] of Int;
   END_VAR
   VAR 
      arHistoryAdv : Array[0..#cSizeHistoryAdv] of Struct
         nStepNum : Int;
         tTimeStamp {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
      END_STRUCT;
   END_VAR
   VAR RETAIN
      nPosHistory : USInt;
      tOnrTimeInStep {InstructionName := 'TONR_TIME'; LibVersion := '1.0'} : TONR_TIME;
      tOnrUnivTimer {InstructionName := 'TONR_TIME'; LibVersion := '1.0'} : TONR_TIME;
      tOnUnivTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
      xStepchange : Bool;
   END_VAR
   VAR 
      R_TRIG_TestMode {InstructionName := 'R_TRIG'; LibVersion := '1.0'} : R_TRIG;
      F_TRIG_TestMode {InstructionName := 'F_TRIG'; LibVersion := '1.0'} : F_TRIG;
      R_TRIG_FunctionEn {InstructionName := 'R_TRIG'; LibVersion := '1.0'} : R_TRIG;
   END_VAR

   VAR_TEMP 
      t_Status : Int;
   END_VAR

   VAR CONSTANT 
      cSizeHistory : USInt := 50;
      cSizeHistoryAdv : UInt := 200;
   END_VAR


BEGIN
	// #R_TRIG_TestMode(CLK := #xInTestMode);
	// #F_TRIG_TestMode(CLK:=#xInTestMode);
	//#R_TRIG_FunctionEn(CLK := #xInFunctionEnabled);
	
	
	REGION Reset
	    IF #xInReset (* OR "FirstScan" *) (* OR #R_TRIG_TestMode.Q OR #F_TRIG_TestMode.Q *) THEN //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	        #nInOutState := 0;
	        #nStateLast := #stInSettings.nStartState;
	        #nStateBeforeError := #stInSettings.nStartState;
	    END_IF;
	END_REGION
	
	REGION Stephistory schreiben
	    IF #nInOutState <> #nStateLast AND
	        #nInOutState <> 0000 THEN
	        #arHistory[#nPosHistory] := #nInOutState;
	        IF #nPosHistory >= #cSizeHistory THEN
	            #arHistory[0] := 9999;
	            #nPosHistory := 0;
	        ELSE
	            #arHistory[#nPosHistory + 1] := 9999;
	            #nPosHistory += 1;
	        END_IF;
	    END_IF;
	END_REGION
	
	REGION Stephistory mit Zeitstempel
	        // IF
	        //     #nInOutState <> #nStateLast
	        // THEN
	        //     #t_Status := MOVE_BLK_VARIANT(SRC := #"arHistoryAdv",
	        //                                   COUNT := #cSizeHistoryAdv,
	        //                                   SRC_INDEX := 0,
	        //                                   DEST_INDEX := 1,
	        //                                   DEST => #"arHistoryAdv");
	        //     #t_Status := RD_LOC_T(#"arHistoryAdv"[0].tTimeStamp); //Zeitstempel
	        //     #"arHistoryAdv"[0].nStepNum := #nInOutState;
	        //     #nStateLast := #nInOutState;
	        // END_IF;
	END_REGION
	
	
	REGION Ein- und Ausschalten
	    //Bei Ein - Auschalten in Schritt 0 und zurück wechseln
	    IF NOT #xInFunctionEnabled
	         AND #nInOutState <> 0000  THEN
	        #nStateBeforeError := #nInOutState;
	        #nInOutState := 0000;
	    ELSIF #xInFunctionEnabled  AND
	        #nInOutState = 0000  THEN
	        IF #stInSettings.xRestartAtStartState THEN
	            #nInOutState := #stInSettings.nStartState;
	        ELSE
	            #nInOutState := #nStateBeforeError;
	        END_IF;
	    END_IF;
	END_REGION
	
	
	REGION Stepchange
	    IF #nInOutState <> #nStateLast THEN
	        IF #nInOutState <> 0000 THEN
	            #nStateLast := #nInOutState;
	            IF #nStateLast <> 0000 THEN
	                #xStepchange := TRUE;
	            END_IF;
	        END_IF;
	    ELSE
	        #xStepchange := FALSE;
	    END_IF;
	END_REGION
	
	REGION Timer
	    #tOnrTimeInStep(IN := #nInOutState <> 0000,
	                    PT := T#+24d_20h_31m_23s,   //Max Zeit für "Time"
	                    R := #xStepchange OR #xInReset);
	    
	    IF #stInSettings.xActUnivTOn THEN
	        #tOnrUnivTimer(IN := #xInRunUnivTOn,
	                      PT := T#+24d_20h_31m_23s);    //Max Zeit für "Time"
	        
	       IF #xInReset OR #xInTimerReset THEN
	            RESET_TIMER(#tOnrUnivTimer);
	        END_IF;
	    END_IF;
	END_REGION
	
	REGION Ausgänge schreiben
	    #xOutStepchange := #xStepchange;
	    #tOutTimeInStep := #tOnrTimeInStep.ET;
	    #nOutStateLast := #nStateLast;
	    
	    IF #stInSettings.xActUnivTOn THEN
	        #tOutUnivTonR := #tOnrUnivTimer.ET;
	    ELSE
	        #tOutUnivTonR := T#0S;
	    END_IF;
	END_REGION
END_FUNCTION_BLOCK

