FUNCTION_BLOCK "FB_Conveyor_Sort"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      xInEnabled : Bool;
      xInFull : Bool;   // If Connected Tracks are Full, Conveyor will set to Suspend
      xInNextConveyorReady : Bool;   // NextConveyor.xOutReadyToReceive
      xInAutomaticMode : Bool;   // nState = E_DeviceState.eExecute
      xInEnergySafeRequest : Bool;   // ConveyorInputSensor
      xInChainBreakMonitoring : Bool;   // ChainBreakSensor
      xInChainBreakMonitoringSmoothing : Bool;
      xInMotorProtectionMonitoring : Bool;   // MotorProtectionInput
      xInSafetyEnable : Bool;   // fbSafetyEStopConveyor
      xInGridSen : Bool;   // Rastersensor
      xInGridQuit : Bool;   // Rasterfehler quittieren
      Settings : Struct
         tEnergySafeTime : Time;
         tStartingTime : Time := T#750ms;
         xGridActive : Bool;   // Überwachung Rastersensor aktiv
         sMotorProtectName : String[10];
      END_STRUCT;
   END_VAR

   VAR_OUTPUT 
      xOutPowerContactor : Bool;   // PowerContactor for Motor
      xOutConveyorFullSpeed : Bool;
      xOutMotStat : Bool;
      xOutReadyToReceive : Bool;   // For xInNextConveyorReady
      xOutLampMotorRunning : Bool;
      eOutState : Int;
      sOutState : String[30];
      xOutInExecute : Bool;
      xOutInSafeState : Bool;
      xOutGridError : Bool;   // Meldeausgang für Rasterfehler
   END_VAR

   VAR_IN_OUT 
      stInOutControlUnitCabinet : "UDT_MainState";
      stInOutInterfaceHMI : "UDT_Conveyor";   // Nur für HMI
   END_VAR

   VAR 
      xErrorChain : Bool;
      xErrorMotorProtection : Bool;
      StartWarningRequestFlag : Bool;
      StartWarningRequest : Bool;
      StateToStartFlag : Bool;
      StateToStart : Bool;
      StateToStopFlag : Bool;
      StateToStop : Bool;
      xReadyToStart : Bool;
      nState : Int := -10;
      nStateHold : Int;   // Aus CRoot --> 
      sStateText : String;
      xEnergySafeRequest : Bool;
      "//TIMER" : Int;
      TOfChainBreakTimer {InstructionName := 'TOF_TIME'; LibVersion := '1.0'} : TOF_TIME;
   END_VAR
   VAR RETAIN
      tChainBreakTime : Time := T#1s;
   END_VAR
   VAR 
      TOnStartingWarningTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Aus CRoot --> 
   END_VAR
   VAR RETAIN
      tStartingWarningTime : Time := T#750ms;
   END_VAR
   VAR 
      TOnEnergySafeTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Aus CRoot --> 
   END_VAR
   VAR RETAIN
      tEnergySafeTime : Time := T#1m;
   END_VAR
   VAR 
      TOnNextConveyorReadyTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Timer On-Delay for xInNextConveyorReady -> Then Suspend
   END_VAR
   VAR RETAIN
      tNextConveyorReadyTime : Time := T#1s;   // Time for Conveyor Ready
   END_VAR
   VAR 
      TOnFullTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Timer On-Delay for Conveyor is Full
   END_VAR
   VAR RETAIN
      tFullTime : Time := T#500ms;   // Time for Conveyor is Full - Init is 3s
   END_VAR
   VAR 
      TONStateTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Aus CRoot --> 
      xStateTimer : Bool;
      tStateTimer : Time;
      xAutomaticMode : Bool;   // xInAutomaticMode + Enable
      fbMonitoringChainBreak : "FB_Monitoring";
      fbMonitoringMotorProtection : "FB_Monitoring";
      TOnGridTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Timer für Rasterüberwachung
      rTrigGrid {InstructionName := 'R_TRIG'; LibVersion := '1.0'} : R_TRIG;   // Flanke von Rastersensor
      xGridError : Bool;   // Fehlerbit bei fehlendem Rastersignal
   END_VAR


BEGIN
	#xEnergySafeRequest := #xInEnergySafeRequest;
	#xAutomaticMode := (#xInAutomaticMode OR #stInOutInterfaceHMI.xSemiAuto) AND #xInEnabled AND NOT #stInOutInterfaceHMI.xDisableConveyor;
	
	REGION RootProperties
	    
	    REGION ErrorChain
	        IF "DB_MachineGlobal".nMachineMode = "E_MachineMode.eOn" THEN
	            #xErrorChain := (NOT #TOfChainBreakTimer.Q) (*OR (ipAlarmSystem.HI (nAlarmNoOffset + 2))*);
	        ELSE
	            #xErrorChain := 0;
	        END_IF;
	    END_REGION
	    
	    REGION ErrorMotorProtection
	        IF "DB_MachineGlobal".nMachineMode = "E_MachineMode.eOn" THEN
	            #xErrorMotorProtection := (NOT #xInMotorProtectionMonitoring) (* OR (ipAlarmSystem.HI (nAlarmNoOffset + 1)) *);
	        ELSE
	            #xErrorMotorProtection := 0;
	        END_IF;
	    END_REGION
	    
	    REGION InExecute
	        #xOutInExecute := #nState >= "E_DeviceState.eExecute";
	    END_REGION
	    
	    REGION InSafeState
	        #xOutInSafeState := #nState <= "E_DeviceState.eIdle";
	    END_REGION
	    
	    REGION ReadyToStart
	        #xReadyToStart := (NOT #xErrorChain) AND
	        (#xInSafetyEnable) AND
	        (NOT #xErrorMotorProtection);
	    END_REGION
	    
	    REGION StartWarningRequest
	        IF #StartWarningRequestFlag THEN
	            #StartWarningRequest := #nState = "E_DeviceState.eStarting";
	            #StartWarningRequestFlag := 0;
	        END_IF;
	    END_REGION
	    
	    REGION StateToStart
	        IF #nState = "E_DeviceState.eIdle" AND #StateToStartFlag THEN
	            #nState := "E_DeviceState.eStarting";
	            #StateToStart := 1; // benötigt?? Wird nirgendwo zurückgesetzt?
	            #StateToStartFlag := 0;
	        END_IF;
	    END_REGION
	    
	    REGION StateToStop
	        IF (#nState > "E_DeviceState.eIdle") AND #StateToStopFlag = 1 THEN
	            #nState := "E_DeviceState.eStopping";
	            #StateToStop := 1; // benötigt?? Wird nirgendwo zurückgesetzt?
	            #StateToStopFlag := 0;
	        END_IF;
	    END_REGION
	END_REGION
	
	REGION StateMain
	    CASE #nState OF
	        "E_DeviceState.eUndefined":
	            REGION Undefined
	                #sStateText := 'StartUp';
	                #nState := "E_DeviceState.eStopped";
	            END_REGION
	            
	        "E_DeviceState.eStopped":
	            REGION Stopped
	                #sStateText := 'Stopped';
	                IF #xReadyToStart AND NOT #Settings.xGridActive THEN
	                    #nState := "E_DeviceState.eIdle";
	                ELSIF #xReadyToStart AND #Settings.xGridActive AND NOT #xGridError THEN
	                    #nState := "E_DeviceState.eIdle";
	                END_IF;
	            END_REGION
	            
	        "E_DeviceState.eIdle":
	            REGION IDLE
	                #sStateText := 'IDLE: Wait';
	                IF NOT #xReadyToStart THEN
	                    #nState := "E_DeviceState.eStopped";
	                ELSIF #xAutomaticMode THEN
	                    #nState := "E_DeviceState.eStarting";
	                END_IF;
	            END_REGION
	            
	        "E_DeviceState.eStarting":
	            REGION Starting
	                #sStateText := 'STARTING: Warning Timer';
	                IF NOT #xAutomaticMode THEN
	                    #nState := "E_DeviceState.eStopping";
	                ELSIF #TOnStartingWarningTimer.Q THEN
	                    #nState := "E_DeviceState.eExecute";
	                    #xEnergySafeRequest := 0;
	                    #xStateTimer := 0;
	                END_IF;
	            END_REGION
	            
	        "E_DeviceState.eExecute":
	            REGION Execute
	                #sStateText := 'EXECUTE: Running';
	                IF NOT #xAutomaticMode THEN
	                    #nState := "E_DeviceState.eStopping";
	                ELSIF #Settings.xGridActive AND #xGridError THEN
	                    #nState := "E_DeviceState.eStopping";
	                ELSIF #TOnEnergySafeTimer.Q THEN
	                    #nState := "E_DeviceState.eHolding";
	                    #nStateHold := "E_DeviceState.eExecute";
	                ELSIF #TOnFullTimer.Q OR #TOnNextConveyorReadyTimer.Q THEN //Könnte man vereinfachen
	                    #nState := "E_DeviceState.eSuspending";
	                END_IF;
	            END_REGION
	            
	            // ------------------------------------------------
	            // HELD
	            // ------------------------------------------------
	        "E_DeviceState.eHolding":
	            #sStateText := 'HOLDING: Start Energy Safing';
	            //         IF #TOnStandstillTimer.Q THEN
	            #nState := "E_DeviceState.eHeld";
	            //         END_IF;
	            
	        "E_DeviceState.eHeld":
	            REGION Held
	                #sStateText := 'HELD: Is Energy Safing';
	                //ipAlarmSystem.Set(nAlarmNoOffset + 0);
	                IF NOT #xAutomaticMode THEN
	                    #nState := "E_DeviceState.eStopping";
	                ELSIF NOT #xInEnergySafeRequest THEN
	                    #nState := "E_DeviceState.eUnholding";
	                END_IF;
	            END_REGION
	            
	        "E_DeviceState.eUnholding":
	            #sStateText := 'Un-Holding: Stops Energy Safing';
	            #xStateTimer := 1;
	            IF #TONStateTimer.Q THEN
	                #nState := #nStateHold;
	                #xStateTimer := 0;
	                //ipAlarmSystem.Clr(nAlarmNoOffset + 0);
	            END_IF;
	            // ------------------------------------------------
	            // SUSPENDED
	            // ------------------------------------------------
	        "E_DeviceState.eSuspending":
	            #sStateText := 'SUSPENDING: This state is a result of a command change from the EXECUTE state.  This state is typically required prior to the SUSPENDED wait state, and prepares the machine (ie stops glue cycles, stops carton feeds, etc) prior to the SUSPEND state.';
	            IF NOT #xInAutomaticMode THEN
	                #nState := "E_DeviceState.eStopping";
	            ELSIF NOT #xInFull THEN
	                #nState := "E_DeviceState.eUnsuspending";
	            END_IF;
	            
	        "E_DeviceState.eSuspended":
	            REGION Suspended
	                #sStateText := 'SUSPENDED: The machine may be running at the relevant setpoint speed, there is no product being produced. This state can be reached as a result of a machine status, and differs from HELD in that HELD is typically a result of an operator request.';
	                //ipAlarmSystem.Set(nAlarmNoOffset + 3);
	                IF NOT #xAutomaticMode THEN
	                    #nState := "E_DeviceState.eStopping";
	                ELSIF #xInNextConveyorReady THEN
	                    #nState := "E_DeviceState.eUnsuspending";
	                END_IF;
	            END_REGION
	            
	        "E_DeviceState.eUnsuspending":
	            #sStateText := 'UN-SUSPENDING: This state is a result of a request from SUSPENDED state to go back to the EXECUTE state.  This actions of this state may include: ramping up speeds, turning on vaccums, the re-engagement of clutches.';
	            #xStateTimer := 1;
	            //#TONStateTimer.PT := #tStandstillTime;
	            IF #TONStateTimer.Q THEN
	                //ipAlarmSystem.Clr (nAlarmNoOffset + 3);
	                #nState := "E_DeviceState.eExecute";
	                #xStateTimer := 0;
	            END_IF;
	        "E_DeviceState.eStopping":
	            REGION Stopping
	                #sStateText := 'STOPPING: This state executes the logic which brings the machine to a controlled and safe stop';
	                #nState := "E_DeviceState.eStopped";
	            END_REGION
	    END_CASE;
	END_REGION
	
	REGION Rastersensor
	    #rTrigGrid(CLK := #xInGridSen);
	    
	    #TOnGridTimer(IN := NOT #rTrigGrid.Q AND #xOutPowerContactor,
	                  PT := T#3s);
	    
	    IF #Settings.xGridActive AND #xOutConveyorFullSpeed AND #TOnGridTimer.Q THEN
	        #xGridError := TRUE;
	    ELSIF (#xGridError AND #xInGridQuit) OR (#xGridError AND NOT #Settings.xGridActive) THEN
	        #xGridError := FALSE;
	    END_IF;
	    
	    #xOutGridError := #xGridError;
	END_REGION
	
	REGION Outputs
	    #xOutPowerContactor := (#nState = "E_DeviceState.eExecute") OR (#nState = "E_DeviceState.eUnsuspending") OR (#nState = "E_DeviceState.eUnholding");
	    #xOutConveyorFullSpeed := #TOnStartingWarningTimer.Q;//#nState = "E_DeviceState.eExecute";
	    #xOutMotStat := #xOutPowerContactor;
	    
	    #xOutLampMotorRunning := (#nState = "E_DeviceState.eStarting") OR
	    (#nState = "E_DeviceState.eExecute") OR
	    (#nState = "E_DeviceState.eHolding") OR
	    (#nState = "E_DeviceState.eUnsuspending") OR
	    (#nState = "E_DeviceState.eStopping");
	    
	    //  ?????????
	    #xOutReadyToReceive := (#nState = "E_DeviceState.eExecute");
	END_REGION
	
	REGION Timer
	    #TOfChainBreakTimer(IN := #xInChainBreakMonitoring,
	                        PT := #tChainBreakTime);
	    
	    #tStartingWarningTime := #Settings.tStartingTime;
	    #TOnStartingWarningTimer(IN := #nState = "E_DeviceState.eStarting" OR #nState = "E_DeviceState.eExecute",
	                             PT := #tStartingWarningTime);
	    
	    // Begrenzen
	    IF #stInOutInterfaceHMI.tEnergySafeTime <= T#0s THEN
	        #stInOutInterfaceHMI.tEnergySafeTime := #tEnergySafeTime;
	    END_IF;
	    
	    #TOnEnergySafeTimer(IN := ((#nState = "E_DeviceState.eExecute") OR (#nState = "E_DeviceState.eSuspending")) AND
	                        (#xInEnergySafeRequest),
	                        PT := (* #stInOutHMI.tEnergySafeTime *)#Settings.tEnergySafeTime);
	    #TOnNextConveyorReadyTimer(IN := NOT #xInNextConveyorReady,
	                               PT := #tNextConveyorReadyTime);
	    #TOnFullTimer(IN := (#nState = "E_DeviceState.eExecute") AND #xInFull,
	                  PT := #tFullTime);
	    
	    #TONStateTimer(IN := #xStateTimer,
	                   PT := #tStateTimer);
	    
	    
	END_REGION
	
	REGION StateStatus
	    CASE #nState OF
	        0..9:
	            #eOutState := "E_DeviceState.eStopped";
	            #sOutState := 'Stopped';
	            (* // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := sw_300_300); *)
	        10..99:
	            #eOutState := "E_DeviceState.eResetting";
	            #sOutState := 'Resetting';
	        100..109:
	            #eOutState := "E_DeviceState.eIdle";
	            #sOutState := 'Idle';
	            (* // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := grau_300_300); *)
	        110..999:
	            #eOutState := "E_DeviceState.eStarting";
	            #sOutState := 'Starting';
	            // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := gelb_300_300);
	        1000..1999:
	            #eOutState := "E_DeviceState.eExecute";
	            #sOutState := 'Execute';
	            // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := gruen_300_300);
	        2000..2499:
	            #eOutState := "E_DeviceState.eCompleting";
	            #sOutState := 'Completing';
	            // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := gelb_300_300);
	        2500..2999:
	            #eOutState := "E_DeviceState.eComplete";
	            #sOutState := 'eComplete';
	            // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := ws_300_300);
	        3000..3499:
	            #eOutState := "E_DeviceState.eHolding";
	            #sOutState := 'Holding';
	        3500..3599:
	            #eOutState := "E_DeviceState.eHeld";
	            #sOutState := 'Held';
	        3600..3999:
	            #eOutState := "E_DeviceState.eUnholding";
	            #sOutState := 'Unholding';
	        4000..4499:
	            #eOutState := "E_DeviceState.eSuspending";
	            #sOutState := 'Suspending';
	        4500..4599:
	            #eOutState := "E_DeviceState.eSuspended";
	            #sOutState := 'Suspended';
	            // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := blau_300_300);
	        4600..4999:
	            #eOutState := "E_DeviceState.eUnsuspending";
	            #sOutState := 'Unsuspending';
	        5000..5999:
	            #eOutState := "E_DeviceState.eStopping";
	            #sOutState := 'Stopping';
	            // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := rot_300_300);
	        6000..6499:
	            #eOutState := "E_DeviceState.eAborting";
	            #sOutState := 'Aborting';
	        6500..6599:
	            #eOutState := "E_DeviceState.eAborted";
	            #sOutState := 'Aborted';
	        6600..6999:
	            #eOutState := "E_DeviceState.eClearing";
	            #sOutState := 'Clearing';
	        10000..11000:
	            #eOutState := "E_DeviceState.eHeld";
	            #sOutState := 'Held';
	            // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := rot_300_300);
	        ELSE
	            #eOutState := "E_DeviceState.eUndefined";
	            #sOutState := 'Undefined';
	            // #sStateColor := F_ImageSelectString(sImagePool := MachineParameter.cImagePoolPath, nImageNo := rot_300_300);
	    END_CASE;
	END_REGION
	
	REGION Visu
	    
	    #stInOutInterfaceHMI.nState := #nState;
	    #stInOutInterfaceHMI.sStateText := #sStateText;
	    
	    REGION Status
	        IF #xReadyToStart THEN // Keine Fehler (Safety, ChainBreak, MotorProtection)
	            CASE #nState OF
	                "E_DeviceState.eUndefined", "E_DeviceState.eStopping", "E_DeviceState.eStopped", "E_DeviceState.eIdle":
	                    #stInOutInterfaceHMI.bStatus := 0; // NOT_AVAILABLE
	                "E_DeviceState.eStarting", 500, "E_DeviceState.eExecute", "E_DeviceState.eHolding", "E_DeviceState.eHeld", "E_DeviceState.eUnholding", "E_DeviceState.eSuspending", "E_DeviceState.eSuspended", "E_DeviceState.eUnsuspending":
	                    #stInOutInterfaceHMI.bStatus := 1; // AVAILABLE
	                ELSE  //Fallback
	                    #stInOutInterfaceHMI.bStatus := 0; // NOT AVAILABLE
	            END_CASE;
	        ELSE // Fehler
	            #stInOutInterfaceHMI.bStatus := 2; // ERROR
	        END_IF;
	    END_REGION
	    
	    REGION Modes
	        // Int16 Bit-codiert
	        #stInOutInterfaceHMI.nMode.%X0 := (#nState = "E_DeviceState.eUndefined") OR (#nState = "E_DeviceState.eStopped") OR (#nState = "E_DeviceState.eIdle"); // The device is turned off
	        #stInOutInterfaceHMI.nMode.%X1 := (#nState = "E_DeviceState.eStarting") OR (#nState = "E_DeviceState.eExecute") OR (#nState = "E_DeviceState.eUnholding"); // The device is turned on
	        #stInOutInterfaceHMI.nMode.%X2 := #stInOutInterfaceHMI.xDisableConveyor OR #stInOutInterfaceHMI.xManual OR #stInOutInterfaceHMI.xSemiAuto OR (#nState = 500) (* OR #xViSuManualMode OR #xViSuSemiAutoMode *); // The device is being manually operated
	        #stInOutInterfaceHMI.nMode.%X3 := (#nState = "E_DeviceState.eHeld"); //The device only is on when the device is needed (like standby)
	        #stInOutInterfaceHMI.nMode.%X10 := (#nState = "E_DeviceState.eSuspended"); // Overflow Error is set for this device
	    END_REGION
	    
	    #stInOutInterfaceHMI.xIsInAutomatic := #xAutomaticMode;
	    #stInOutInterfaceHMI.xEnergySaveReq := (#xInEnergySafeRequest);
	    #stInOutInterfaceHMI.xPowerContactor := #xOutPowerContactor;
	    #stInOutInterfaceHMI.xPluggedChainBreak := #Settings.xGridActive;
	    #stInOutInterfaceHMI.xStatusChainBreak := (#Settings.xGridActive AND NOT #xGridError) OR NOT #Settings.xGridActive;
	    #stInOutInterfaceHMI.xStatusMotorProtect := #xInMotorProtectionMonitoring;
	    #stInOutInterfaceHMI.xConveyorFull1 := FALSE;
	    #stInOutInterfaceHMI.xConveyorFull2 := FALSE;
	    #stInOutInterfaceHMI.xReady2Receive := TRUE;
	    #stInOutInterfaceHMI.xNextConveyorReady := TRUE;
	    
	END_REGION
	
	REGION Monitoring
	    #fbMonitoringChainBreak.stSettings.nErrorType := 1;
	    #fbMonitoringChainBreak.stSettings.xErrLedIfMachineIsOff := TRUE;
	    #fbMonitoringChainBreak.stSettings.xSelfQuit := TRUE;
	    #fbMonitoringChainBreak(sInFctnName := 'Sort Conveyor',
	                            InOutMachineState := #stInOutControlUnitCabinet,
	                            xInQuitError := #stInOutControlUnitCabinet.xQuitError,
	                            xInSignal := NOT #xErrorChain,
	                            sInAlarmMessage := 'Kettenspannung nicht ausreichend oder Kette gebrochen! Ueberpruefen Sie die Kette.');
	    
	    #fbMonitoringMotorProtection.stSettings.nErrorType := 1;
	    #fbMonitoringMotorProtection.stSettings.xErrLedIfMachineIsOff := TRUE;
	    #fbMonitoringMotorProtection.stSettings.xSelfQuit := TRUE;
	    #fbMonitoringMotorProtection.stSettings.tDelay := T#1s;
	    #fbMonitoringMotorProtection(sInPrefix := #stInOutInterfaceHMI.sConveyorName,
	                                 wInTextListId_Function := 400,
	                                 wInTextListId_Message := 401,
	                                 sInSuffix := #Settings.sMotorProtectName,
	                                 InOutMachineState := #stInOutControlUnitCabinet,
	                                 xInQuitError := #stInOutControlUnitCabinet.xQuitError,
	                                 xInSignal := #xInMotorProtectionMonitoring);
	END_REGION
END_FUNCTION_BLOCK

