FUNCTION_BLOCK "FB_Conveyor"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      xInEnabled : Bool := true;   // This is for enabling or disabling this conveyor - e.G. for Testing
      xInAutomaticMode : Bool;   // When DriveGroup is in Automatic-Mode
      xInMotorProtectionMonitoring : Bool;   // Motor-Circuite-Breaker Monitoring; 0 = Error
      xInRequestRun : Bool;
      xInManualMode : Bool;   // HMI Override
      xInGridSen : Bool;   // Rastersensor
      xInGridQuit : Bool;   // Rasterfehler quittieren
      Settings : Struct
         tEnergySafeTime : Time := T#60s;
         tStartingTime : Time := T#50ms;
         xGridActive : Bool;   // Überwachung Rastersensor aktiv
         sGridSenName : String[10];   // für fehlermeldung
         sMotorProtectName : String[10];   // für fehlermeldung
      END_STRUCT;
   END_VAR

   VAR_OUTPUT 
      xOutPowerContactor : Bool;   // Power contactor for motor
      xOutConveyorFullSpeed : Bool;
      xOutGridError : Bool;   // Meldeausgang für Rasterfehler
   END_VAR

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

   VAR 
      nState : Int := -10;
      nStateHold : Int := -10;
      sStateText : String;
      xReadyToStart : Bool;
      xAutomaticMode : Bool;
      TOnStartUpWarningTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
      tStartUpTime : Time := T#50ms;
      TOnEnergySafeTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
      tEnergySafeTime : Time := T#60s;
      TOnStandstillTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
      tStandstillTime : Time := T#50ms;
      TONStateTimer {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
      xStateTimer : Bool;
      tStateTimer : Time;
      tRestlaufzeit : Time;
      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
      fbMonitoringGridError : "FB_Monitoring";
      fbMonitoringMotorProtection : "FB_Monitoring";
   END_VAR


BEGIN
	REGION Automatic Mode
	    #xAutomaticMode := (#xInAutomaticMode OR #stInOutInterfaceHMI.xSemiAuto) AND #xInEnabled AND NOT #stInOutInterfaceHMI.xDisableConveyor;
	END_REGION
	
	REGION ReadyToStart
	    #xReadyToStart := #xInMotorProtectionMonitoring AND #xInEnabled;
	END_REGION ;
	// ------------------------------------------------
	// Main State Machine
	// ------------------------------------------------
	REGION MainStateMachine
	    CASE #nState OF
	        "E_DeviceState.eUndefined":
	            REGION Undefined
	                #sStateText := 'StartUp';
	                #nState := "E_DeviceState.eStopped";
	                #xStateTimer := 0;
	            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";
	                ELSIF #xInManualMode THEN
	                    #nState := 500;
	                END_IF;
	            END_REGION ;               
	        "E_DeviceState.eStarting":
	            REGION Starting        
	                #sStateText := 'STARTING: Warning Timer';
	                IF NOT #xAutomaticMode THEN
	                    #nState := "E_DeviceState.eStopping";
	                ELSIF #TOnStartUpWarningTimer.Q THEN
	                    #nState := "E_DeviceState.eExecute";
	                    //#bInRequestRun := 0;
	                END_IF;
	            END_REGION ;               
	        500:
	            REGION Manual Mode        
	                #sStateText := 'JOG: Manual Mode';
	                IF NOT #xInManualMode THEN
	                    #nState := "E_DeviceState.eStopping";
	                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";
	                END_IF;
	            END_REGION ;             
	            // ------------------------------------------------
	            // HELD
	            // ------------------------------------------------
	            
	        "E_DeviceState.eHolding":
	            REGION Holding
	                #sStateText := 'HOLDING: Start Energy Safing';
	                IF #TOnStandstillTimer.Q THEN
	                    #nState := "E_DeviceState.eHeld";
	                END_IF;
	            END_REGION
	            
	        "E_DeviceState.eHeld":
	            REGION Held
	                #sStateText := 'HELD: Is Energy Safing';
	                //ipAlarmSystem.Set(nAlarmNoOffset + 0);
	                IF NOT #xAutomaticMode THEN
	                    #nState := "E_DeviceState.eStopping";
	                ELSIF #xInRequestRun THEN
	                    #nState := "E_DeviceState.eUnholding";
	                END_IF;
	            END_REGION
	            
	        "E_DeviceState.eUnholding":
	            REGION Unholding 
	                #sStateText := 'Un-Holding: Stops Energy Safing';
	                #xStateTimer := 1;
	                #tStateTimer := #tStandstillTime;
	                IF #TONStateTimer.Q THEN
	                    #nState := #nStateHold;
	                    #xStateTimer := 0;
	                    //ipAlarmSystem.Clr (nAlarmNoOffset + 0);
	                END_IF;
	            END_REGION
	            // ------------------------------------------------
	            // STOPPING
	            // ------------------------------------------------
	            
	        "E_DeviceState.eStopping":
	            REGION Stopping 
	                #sStateText := 'Stopping';
	                IF #TOnStandstillTimer.Q THEN
	                    //ipAlarmSystem.Clr (nAlarmNoOffset + 0);
	                    //ipAlarmSystem.Clr (nAlarmNoOffset + 3);
	                    #nState := "E_DeviceState.eStopped";
	                END_IF;
	                #xStateTimer := 0;
	            END_REGION
	    END_CASE;
	END_REGION
	//------------------------------------------------  
	// Stop Conveyor by ChainBreakMonitor and MotorProtection
	//------------------------------------------------  
	IF (NOT #xReadyToStart) AND
	    (#nState > "E_DeviceState.eIdle") AND
	    (#nState < "E_DeviceState.eStopping") THEN
	    #nState := "E_DeviceState.eStopping";
	END_IF;
	
	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
	//------------------------------------------------  
	// Outputs
	//------------------------------------------------  
	REGION Outputs
	    #xOutPowerContactor :=
	    ((#nState = "E_DeviceState.eExecute") OR
	    //(nState = E_DeviceState.eUnsuspending) OR
	    (#nState = "E_DeviceState.eUnholding") OR
	    (#nState = "E_DeviceState.eSuspending") OR
	    #stInOutInterfaceHMI.xManual OR
	    (#nState = 500));
	    #xOutConveyorFullSpeed := #nState = "E_DeviceState.eExecute";
	END_REGION ;
	(*
	bOutLampMotorRunning := (#nState = "E_DeviceState.eStarting") OR
	(#nState = "E_DeviceState.eExecute") OR
	(#nState = "E_DeviceState.eHolding") OR
	(#nState = "E_DeviceState.eSuspending") OR
	(#nState = "E_DeviceState.eUnsuspending") OR
	(#nState = "E_DeviceState.eStopping");
	*)
	//------------------------------------------------  
	// Timer
	//------------------------------------------------  
	REGION Timer
	    #tStartUpTime := #Settings.tStartingTime;
	    #TOnStartUpWarningTimer(IN := #nState = "E_DeviceState.eStarting",
	                            PT := #tStartUpTime);
	    #TOnEnergySafeTimer(
	                        IN := ((#nState = "E_DeviceState.eExecute") OR (#nState = "E_DeviceState.eSuspending")) AND
	                        (NOT #xInRequestRun),
	                        ET => #tRestlaufzeit,
	                        PT := #Settings.tEnergySafeTime);
	    #TOnStandstillTimer(IN := (#nState = "E_DeviceState.eStopping") OR (#nState = "E_DeviceState.eHolding"),
	                        PT := #tStandstillTime);
	    #TONStateTimer(IN := #xStateTimer,
	                   PT := #tStateTimer);
	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 := (NOT #xInRequestRun);
	    #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
	    
	    REGION Grid Error
	        #fbMonitoringGridError.stSettings.nErrorType := 1;
	        #fbMonitoringGridError.stSettings.xErrLedIfMachineIsOff := TRUE;
	        #fbMonitoringGridError.stSettings.xSelfQuit := TRUE;
	        #fbMonitoringGridError.stSettings.tDelay := T#1s;
	        #fbMonitoringGridError(sInPrefix := #stInOutInterfaceHMI.sConveyorName,
	                               wInTextListId_Function := 400,
	                               wInTextListId_Message := 400,
	                               sInSuffix := #Settings.sMotorProtectName,
	                               InOutMachineState := #stInOutControlUnitCabinet,
	                               xInQuitError := #xInGridQuit,
	                               xInSignal := NOT #xGridError,
	                               sInAlarmMessage := 'Fehler Rastersensorueberwachung');
	    END_REGION
	
	    
	    REGION Motorschutz
	        #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,
	                                     sInAlarmMessage := 'Fehler Motorschutzschalter');
	    END_REGION
	
	    
	END_REGION
END_FUNCTION_BLOCK

