FUNCTION_BLOCK "FB_StockRemovalBLKModul"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      n_InBlkID : UInt;
      x_InStartBlk : Bool;
      x_InQuit : Bool;
      n_JamCapacityAMR : UInt;
      n_JamCapacityAMRCriticalAmount : UInt;
      n_JamCapacityF2H : UInt;
      n_JamCapacityF2HCriticalAmount : UInt;
      n_JamCapacityEmpty : UInt;
      n_JamCapacityEmptyCriticalAmount : UInt;
      sIn_ObjectName : String[50];
   END_VAR

   VAR_IN_OUT 
      arrudt_Line : Array[1.."cNoLines"] of "stStorageLine";
      arrJamCarrier : Array[*] of "stCarrier";
      str_InOut_FillingChart : Struct
         n_BLK : Int;
         udt_LineLvlBlk : "udt_LineLvl";
      END_STRUCT;
      str_InOut_EmptyChart : Struct
         n_BLK : Int;
         udt_LineLvlBlk : "udt_LineLvl";
      END_STRUCT;
      x_Error : Bool;
      n_ErrorID : Int;
      n_ErrorBLKID : Int;
      udt_InOutMFRTRO : "UDT_MFR_StoreTRO";
      udt_HMI : "udt_LinienSortierungHMI";
      udt_INOUT_Handshake : "udt_StockRemovalBlkHandshake";
      udt_INOUT_HandshakeEmpty : "udt_StockRemovalBlkHandshake";
      udt_InOutHandshakeMessageToMes : "udt_HandshakeMessageToMES";
   END_VAR

   VAR 
      n_NextLine { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : USInt;
      n_RearrangeSequence : UInt;
      n_CollectLineIDXSave : UInt;
      n_filledLineCounter : UInt;
      tonr_StartRemoval {InstructionName := 'TONR_TIME'; LibVersion := '1.0'; S7_SetPoint := 'False'} : TONR_TIME;
      nMaxFilledLine : USInt;
      nMaxFilledLineSAve : USInt;
      n_filledLineCounter_1 : USInt;
      n_rearrangeLineCounter : USInt;
      n_CollectLineIDX : USInt;
      n_GetCarrierAmountIDX : Int;
      n_GetCarrierAmount : USInt;
      n_CheckCarrierOnListIDX : Int;
      n_StockRemovalSequence : UInt;
      n_SearchSortedBlkIDXSave : UInt;
      n_SearchSortedLineIDXSave : UInt;
      n_SearchSortedBlkIDX : USInt;
      n_SearchSortedLineIDX : USInt;
      n_DestiantionIDX : Int;
      str_CurrentTask : String[50];
      PrgAlarm_Failmessage {InstructionName := 'Program_Alarm'; LibVersion := '1.0'; S7_SetPoint := 'False'} : Program_Alarm;
      wsErrorMessage : WString[50];
      n_CurrentDonorLine : Int;   // n_CurrentDonorLine
      n_CurrentDonorCount : Int;   // Anzahl Carrier in dieser Donorlinie
      n_CurrentDonorType : Int;   // 1=EmptyCarrier, 2=LoadedCarrier
      n_CurrentTakerLine : Int;   // Aktuelle Takerlinie
      n_CurrentTakerFree : Int;   // Freie Slots in der Takerlinie
      n_SelectedDonorPos : Int;   // Position des Carriers in der Donorlinie
      n_SelectedTakerPos : Int;   // Freier Slot in der Takerlinie
      n_RearrangedCounter : Int;   // Anzahl bereits verschobener Carrier
      n_RemainingDonor : Int;   // Restanzahl Carrier in Donorlinie
      n_MaxEmptyLine : Int;
      lfb_MessageToMES { S7_SetPoint := 'False'} : "FB_UpdateMessage";
      str_MessageUpdate : Struct
         str_Ctrl : Struct
            x_SendMessage : Bool;
            arrstr_MessageData : Array[1.."cNoPositions"] of Struct
               n_MessageType : USInt;
               n_PartID : DInt;
            END_STRUCT;
         END_STRUCT;
         str_State : Struct
            x_Ready : Bool;
            x_Busy : Bool;
            x_Done : Bool;
         END_STRUCT;
      END_STRUCT;
      udt_Handshake_intern { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : "udt_StockRemovalBlkHandshake";
   END_VAR

   VAR_TEMP 
      nTempRetVal : Int;
      tTempLocalTime {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
      nTempCntEmpty : Int;   // empty counter
      nTempCntLoaded : Int;   // loaded counter
      nTempCntFree : Int;   // free counter
      t_Typ : Int;
      t_donorMetric : Int;
      idxLineAnalyze : Int;   // Linienindex bei Analyse (Typbestimmung)
      idxLineSearchTaker : Int;   // Linienindex bei Suche nach Taker
      idxCarrierAnalyze : Int;   // Carrierindex beim Analysieren einer Linie
      idxCarrierSearchTaker : Int;   // Carrierindex bei Analyse einer Taker-Linie
      idxCarrierDonorFind : Int;   // Carrierindex beim Finden der Quellposition (Donor)
      idxCarrierTakerFind : Int;   // Carrierindex beim Finden der Zielposition (Taker)
      idxCarrierDonorCheck : Int;   // Carrierindex beim Prüfen der Donor-Linie nach Umlagerung
      nTempUpperBound : DInt;
      nTempLowBound : DInt;
   END_VAR

   VAR CONSTANT 
      "~~~~~~~~MessageType~~~~~~" : Bool;
      c01_PartFound : USInt := 1;
      c02_PartNotFound : USInt := 2;
      "~~~~~~~~RearrangeSequence~~~~~" : Bool;
      c0000_Wait : UInt;
      c0005_WaitForTimer : UInt := 5;
      c0010_SearchLine : UInt := 10;
      c0020_CalculateAmount : UInt := 20;
      c0030_StartRearrange : UInt := 30;
      c0040_CheckCarrier : UInt := 40;
      c0045_SendMessage : UInt;
      c0050_ResetTRO : UInt := 50;
      c0060_CheckMoreLines : UInt := 60;
      c0070_StartStockRemove : UInt := 70;
      c0075_WaitStockRemovalDone : UInt := 75;
      c0080_ResetRemoval : UInt := 80;
      c0090_SetDone : UInt := 90;
      c0900_Error : UInt := 900;
      c0100_SearchLineAdvanced : UInt := 100;
      c0110_SearchTakerLine : UInt := 110;
      c0120_SelectCarrierFromDonor : UInt := 120;
      c0130_FindFreeTakerPos : UInt := 130;
      c0140_MoveCarrier : UInt := 140;
      c0143_WaitTRODone : UInt := 143;
      c0145_ResetTRO : UInt := 145;
      c0150_CheckDonorEmpty : UInt := 150;
      c0160_Done : UInt := 160;
      c0170_NoTakerFound : UInt := 170;
      c0175_NoCarrierFound : UInt := 175;
      "~~~~~ErrorID~~~~~" : Int;
      c0010_NoLineFound : Int := 10;
      "~~~~~StockRemovalSequence~~~~~" : Int;
      c0020_CheckCapacity : UInt := 20;
      c0025_SetDestination : UInt := 25;
      c0030_SetTRO : UInt := 30;
      c0040_WaitTRODone : UInt := 40;
      c0100_SetDestinationEmpty : UInt := 100;
      c0110_SetTRO : UInt := 110;
      c0120_WaitTRODone : UInt := 120;
      c0130_ResetTRO : UInt := 130;
      "~~~~~~Destination~~~~~" : UInt;
      c001_AMR : Int := 1;
      c002_F2H : Int := 2;
      c003_EmptyCarrier : Int := 3;
      "~~~~~~~CarrierStatusbit~~~~~~~~~~~~~~~~~" : Int;
      cCarrLoaded : Word := 16#0002;
      "~~~~~~~LinienTypen~~~~~~~~~" : Word;
      cLineFree : Int;
      cLineEmpty : Int := 1;
      cLineLoaded : Int := 2;
      cLineMixed : Int := 3;
      cLckFull : Byte := 16#04;   // %X2 Full Carrier
      cLckEmpty : Byte := 16#08;   // %X3 EmptyCarrier
      cLckMixed : Byte := 16#10;   // %X4 MixedCarrier
   END_VAR


BEGIN
	
	REGION Info
	(* Company: Schönenberger Systeme GmbH / (C) Copyright (2022). All rights reserved.
	-----------------------------------------------------------------------------------------
	Title: FB_StockRemovalBLKModul
	Comment/Function: manages stock removal for a single block
	Library/Family: -
	Author: department: "Automatisierung" ; person in charge / contact: C.Nottelmann (Automated Intelligence)
	Target System: CPU 1518F-4 PN/DP
	Engineering: TIA Portal V20
	Restrictions: -
	Requirements: -
	-----------------------------------------------------------------------------------------
	Change log table:
	Version     | Date       | Signature (Safety) | Expert in charge | Changes applied
	------------|------------|--------------------|------------------|-----------------------
	001.000.000 | 2025-07-28 | -                  |                  | Header added on 28.07.2025
	                                                                   
	*)
	END_REGION
	
	REGION LocalTime
	    // read local time for timestamp ------------------------------------------
	    #nTempRetVal := RD_LOC_T(#tTempLocalTime);
	END_REGION
	
	REGION Times
	    
	    #tonr_StartRemoval(IN:=#n_RearrangeSequence = #c0005_WaitForTimer,
	                       PT:=#udt_HMI.udt_BlockRemovalModul.str_Ctrl.t_StartDelayTime);
	    
	END_REGION
	
	REGION Check Array
	    #nTempUpperBound := UPPER_BOUND(ARR := #arrJamCarrier, DIM := 1);
	    #nTempLowBound := LOWER_BOUND(ARR := #arrJamCarrier, DIM := 1);
	    
	END_REGION
	
	
	//gibt Rückmeldung das er startet über den Handshake, alle anderen werden gesperrt.
	//Alle sperren und per Fpr Schleife den Rückmeldenden starten
	//
	
	
	
	REGION Umlagern
	    // Carrier beim Umlagern mit Ziel versehen
	    CASE #n_RearrangeSequence OF
	        #c0000_Wait:
	            REGION c0000_Wait
	                ///Action
	                #x_Error := FALSE;
	                #n_ErrorID := 0;
	                #n_ErrorBLKID := 0;
	                #udt_INOUT_Handshake.str_State.b_Busy := False;
	                #n_CollectLineIDXSave := 1; //Rücksetzen des gespeicherten Index
	                #n_filledLineCounter := 1;
	                #str_CurrentTask := 'Bereit zum Einlagern';
	                //Transition
	                IF #udt_INOUT_Handshake.str_Ctrl.b_Start OR (#udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_StartRearrange
	                    AND NOT #udt_INOUT_Handshake.str_Ctrl.b_Start
	                    AND NOT #udt_INOUT_HandshakeEmpty.str_Ctrl.b_Start)
	                    OR #udt_INOUT_HandshakeEmpty.str_Ctrl.b_Start
	                THEN
	                    #n_RearrangeSequence := #c0005_WaitForTimer;
	                END_IF;
	            END_REGION
	            
	        #c0005_WaitForTimer:
	            REGION c0005_WaitForTimer
	                //Action
	                #udt_INOUT_Handshake.str_State.b_Busy := True;
	                
	                //Transition
	                IF #tonr_StartRemoval.Q AND NOT #udt_INOUT_HandshakeEmpty.str_Ctrl.b_Start
	                THEN
	                    #n_RearrangeSequence := #c0010_SearchLine;
	                ELSIF #udt_INOUT_HandshakeEmpty.str_Ctrl.b_Start
	                THEN
	                    ;
	                ELSIF #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_Cancel
	                THEN
	                    #n_RearrangeSequence := #c0000_Wait;
	                END_IF;
	            END_REGION
	            
	        #c0010_SearchLine:
	            REGION c0010_SearchLine
	                //Action
	                
	                #nMaxFilledLineSAve := #str_InOut_FillingChart.udt_LineLvlBlk.arr_OrderLines[#n_filledLineCounter];
	                #str_CurrentTask := 'Leere Linie zum Umsortieren wird gesucht';
	                
	                FOR #n_CollectLineIDX := 2 TO 21 DO
	                    IF #arrudt_Line[#n_CollectLineIDX].stData.Status.nCount = 0
	                    THEN
	                        #arrudt_Line[#n_CollectLineIDX].stData.Status.bByte.%X5 := True; // zum Sortieren reserviert
	                        #n_CollectLineIDXSave := #n_CollectLineIDX;
	                        EXIT;
	                    END_IF;
	                END_FOR;
	                
	                
	                //Transition
	                IF #arrudt_Line[#n_CollectLineIDXSave].stData.Status.bByte.%X5
	                THEN
	                    #n_RearrangeSequence := #c0020_CalculateAmount;
	                ELSIF NOT #arrudt_Line[#n_CollectLineIDXSave].stData.Status.bByte.%X5
	                THEN
	                    #n_RearrangeSequence := #c0100_SearchLineAdvanced;
	                END_IF;
	            END_REGION
	            
	            
	            
	        #c0020_CalculateAmount:
	            REGION c0020_CalculateAmount
	                //Action
	                FOR #n_GetCarrierAmountIDX := 1 TO "cNoPositions"  DO
	                    IF #arrudt_Line[#nMaxFilledLineSAve].arCarrier[#n_GetCarrierAmountIDX].bStatus.%X5
	                    THEN
	                        IF #arrudt_Line[#nMaxFilledLineSAve].arCarrier[#n_GetCarrierAmountIDX].bStatus.%X8 AND #n_JamCapacityAMR > #n_JamCapacityAMRCriticalAmount
	                        THEN
	                            #arrudt_Line[#nMaxFilledLineSAve].arCarrier[#n_GetCarrierAmountIDX].nDestination := UINT_TO_INT(#n_InBlkID * 100 + #n_CollectLineIDXSave);
	                        ELSIF #arrudt_Line[#nMaxFilledLineSAve].arCarrier[#n_GetCarrierAmountIDX].bStatus.%X9 AND #n_JamCapacityF2H > #n_JamCapacityF2HCriticalAmount
	                        THEN
	                            #arrudt_Line[#nMaxFilledLineSAve].arCarrier[#n_GetCarrierAmountIDX].nDestination := UINT_TO_INT(#n_InBlkID * 100 + #n_CollectLineIDXSave);
	                        ELSE
	                            #arrudt_Line[#nMaxFilledLineSAve].arCarrier[#n_GetCarrierAmountIDX].nDestination := UINT_TO_INT(#n_InBlkID * 100 + #nMaxFilledLineSAve);
	                        END_IF;
	                        
	                        #n_GetCarrierAmount := #n_GetCarrierAmountIDX;
	                    END_IF;
	                    
	                END_FOR;
	                
	                //Transition
	                #n_RearrangeSequence := #c0030_StartRearrange;
	            END_REGION
	            
	        #c0030_StartRearrange:
	            REGION c0030_StartRearrange
	                //Action
	                #udt_InOutMFRTRO.UDT_Ctrl.nAmount := #n_GetCarrierAmount;
	                #udt_InOutMFRTRO.UDT_Ctrl.nMode := 1;
	                #udt_InOutMFRTRO.UDT_Ctrl.xStart := True;
	                #str_CurrentTask := 'Umsortieren gestartet';
	                
	                
	                //Transition
	                #n_RearrangeSequence := #c0040_CheckCarrier;
	            END_REGION
	            
	        #c0040_CheckCarrier:
	            REGION c0040_CheckCarrier
	                //Action
	                //Handshake erforderlich, sonst mehrfach Zählung
	                FOR #n_CheckCarrierOnListIDX := #nTempLowBound TO #nTempUpperBound  DO //"cNoPositions"
	                    IF #udt_InOutMFRTRO.UDT_Scan.nBarcode = #arrJamCarrier[#n_CheckCarrierOnListIDX].nBarcode
	                        AND #arrJamCarrier[#n_CheckCarrierOnListIDX].bStatus.%X5
	                    THEN
	                        #n_rearrangeLineCounter += 1;
	                        #str_MessageUpdate.str_Ctrl.arrstr_MessageData[#n_CheckCarrierOnListIDX].n_MessageType := #c01_PartFound;
	                        #str_MessageUpdate.str_Ctrl.arrstr_MessageData[#n_CheckCarrierOnListIDX].n_PartID := #arrJamCarrier[#n_CheckCarrierOnListIDX].nBarcode;
	                        
	                        ;
	                    END_IF;
	                END_FOR;
	                
	                //Transition
	                IF #udt_InOutMFRTRO.UDT_State.xReady OR #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_Cancel
	                THEN
	                    #n_RearrangeSequence := #c0050_ResetTRO;
	                END_IF;
	            END_REGION
	            
	            
	        #c0050_ResetTRO:
	            REGION c0050_ResetTRO
	                //Action
	                #udt_InOutMFRTRO.UDT_Ctrl.xStart := FALSE;
	                #udt_InOutMFRTRO.UDT_Ctrl.nAmount := 0;
	                #udt_InOutMFRTRO.UDT_Ctrl.nMode := 0;
	                #n_filledLineCounter += 1;
	                IF #n_filledLineCounter > 21
	                THEN
	                    #n_filledLineCounter := 21;
	                END_IF;
	                
	                
	                
	                //Transition
	                #n_RearrangeSequence := #c0060_CheckMoreLines;
	            END_REGION
	            
	        #c0060_CheckMoreLines:
	            REGION c0060_CheckMoreLines
	                //Actions
	                
	                #n_NextLine := #str_InOut_FillingChart.udt_LineLvlBlk.arr_OrderLines[#n_filledLineCounter];
	                IF #n_NextLine = 0
	                THEN
	                    #n_NextLine := 1;
	                END_IF;
	                //Transitions
	                IF #str_InOut_FillingChart.udt_LineLvlBlk.arr_StorageLvl[#n_NextLine] > 0
	                    AND #arrudt_Line[#n_CollectLineIDX].stData.Config.nCapacitySet - #n_rearrangeLineCounter > #str_InOut_FillingChart.udt_LineLvlBlk.arr_StorageLvl[#n_NextLine]
	                    // Absicherung Linie in die umsortiert wird nicht voll  
	                THEN
	                    #n_RearrangeSequence := #c0020_CalculateAmount;
	                ELSE
	                    #arrudt_Line[#n_CollectLineIDXSave].stData.Status.bByte.%X5 := False;
	                    #arrudt_Line[#n_CollectLineIDXSave].stData.Status.bByte.%X6 := TRUE; // Sortieren abgeschlossen
	                    #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_StartRearrange := False;
	                    #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_Cancel := False;
	                    #str_CurrentTask := 'Umsortieren beendet';
	                    #n_RearrangeSequence := #c0070_StartStockRemove;
	                END_IF;
	            END_REGION
	            
	        #c0070_StartStockRemove:
	            //Action
	            #udt_Handshake_intern.str_Ctrl.b_RemoveRelease := True;
	            
	            //Transition
	            IF #udt_Handshake_intern.str_State.b_Busy
	            THEN
	                #n_RearrangeSequence := #c0075_WaitStockRemovalDone;
	            END_IF;
	            
	        #c0075_WaitStockRemovalDone:
	            //Action
	            
	            
	            //Transition
	            IF #udt_Handshake_intern.str_State.b_Done
	            THEN
	                #n_RearrangeSequence := #c0080_ResetRemoval;
	            END_IF;
	            
	        #c0080_ResetRemoval:
	            //Action
	            #udt_Handshake_intern.str_Ctrl.b_RemoveRelease := False;
	            
	            //Transition
	            IF #str_InOut_FillingChart.udt_LineLvlBlk.arr_StorageLvl[#n_NextLine] > 0
	                OR #str_InOut_FillingChart.udt_LineLvlBlk.arr_StorageLvl[#n_NextLine + 1] > 0
	            THEN
	                #n_RearrangeSequence := #c0020_CalculateAmount;
	            ELSE
	                #n_RearrangeSequence := #c0090_SetDone;
	            END_IF;
	            
	        #c0090_SetDone:
	            //Action
	            #udt_INOUT_Handshake.str_State.b_Busy := False;
	            #udt_INOUT_Handshake.str_State.b_Done := True;
	            
	            //Transition
	            IF NOT #udt_INOUT_Handshake.str_Ctrl.b_Start
	            THEN
	                #n_RearrangeSequence := #c0000_Wait;
	            END_IF;
	            
	            
	            
	        #c0100_SearchLineAdvanced:
	            REGION c0100_SearchLineAdvanced
	                //Donorlinie suchen (am wenigsten befüllt, sortenrein)
	                //Action
	                #n_CurrentDonorLine := 0;
	                #n_CurrentDonorCount := 32767;   // "unendlich"
	                #n_CurrentDonorType := 0;        // 1=EmptyCarrier, 2=LoadedCarrier
	                
	                FOR #idxLineAnalyze := 1 TO "cNoLines" DO
	                    #nTempCntLoaded := 0;
	                    #nTempCntEmpty := 0;
	                    
	                    FOR #idxCarrierAnalyze := 1 TO "cNoPositions" DO
	                        IF #arrudt_Line[#idxLineAnalyze].arCarrier[#idxCarrierAnalyze].nBarcode > 0 THEN
	                            IF #arrudt_Line[#idxLineAnalyze].arCarrier[#idxCarrierAnalyze].bStatus.%X1 THEN
	                                #nTempCntLoaded += 1;
	                            ELSE
	                                #nTempCntEmpty += 1;
	                            END_IF;
	                        END_IF;
	                    END_FOR;
	                    
	                    // Linie ist sortenrein LOADED
	                    IF (#nTempCntLoaded > 0) AND (#nTempCntEmpty = 0) THEN
	                        IF #nTempCntLoaded < #n_CurrentDonorCount THEN
	                            #n_CurrentDonorLine := #idxLineAnalyze;
	                            #n_CurrentDonorCount := #nTempCntLoaded;
	                            #n_CurrentDonorType := 2;
	                        END_IF;
	                    END_IF;
	                    
	                    // Linie ist sortenrein EMPTY
	                    IF (#nTempCntEmpty > 0) AND (#nTempCntLoaded = 0) THEN
	                        IF #nTempCntEmpty < #n_CurrentDonorCount THEN
	                            #n_CurrentDonorLine := #idxLineAnalyze;
	                            #n_CurrentDonorCount := #nTempCntEmpty;
	                            #n_CurrentDonorType := 1;
	                        END_IF;
	                    END_IF;
	                END_FOR;
	                
	                // Transition
	                IF #n_CurrentDonorLine > 0 THEN
	                    #n_RearrangeSequence := #c0110_SearchTakerLine;
	                ELSE
	                    #n_RearrangeSequence := #c0170_NoTakerFound;  // Fehlerbehandlung
	                END_IF;
	            END_REGION
	            
	            
	        #c0110_SearchTakerLine:
	            REGION c0110_SearchTakerLine
	                //Takerlinie suchen (kompatibel + freie Slots)
	                //Action
	                #n_CurrentTakerLine := 0;
	                #n_CurrentTakerFree := -1;
	                
	                FOR #idxLineSearchTaker := 1 TO "cNoLines" DO
	                    IF #idxLineSearchTaker = #n_CurrentDonorLine THEN
	                        CONTINUE;
	                    END_IF;
	                    
	                    #nTempCntLoaded := 0;
	                    #nTempCntEmpty := 0;
	                    #nTempCntFree := 0;
	                    
	                    FOR #idxCarrierSearchTaker := 1 TO "cNoPositions" DO
	                        IF #arrudt_Line[#idxLineSearchTaker].arCarrier[#idxCarrierSearchTaker].nBarcode = 0 THEN
	                            #nTempCntFree := #nTempCntFree + 1;
	                        ELSIF #arrudt_Line[#idxLineSearchTaker].arCarrier[#idxCarrierSearchTaker].bStatus.%X1 THEN
	                            #nTempCntLoaded := #nTempCntLoaded + 1;
	                        ELSE
	                            #nTempCntEmpty := #nTempCntEmpty + 1;
	                        END_IF;
	                    END_FOR;
	                    
	                    // Linie ist sortenrein LOADED
	                    IF (#nTempCntLoaded > 0) AND (#nTempCntEmpty = 0) THEN
	                        IF (#n_CurrentDonorType = 2) AND (#nTempCntFree > 0) AND (#nTempCntFree > #n_CurrentTakerFree) THEN
	                            #n_CurrentTakerLine := #idxLineSearchTaker;
	                            #n_CurrentTakerFree := #nTempCntFree;
	                        END_IF;
	                    END_IF;
	                    
	                    // Linie ist sortenrein EMPTY
	                    IF (#nTempCntEmpty > 0) AND (#nTempCntLoaded = 0) THEN
	                        IF (#n_CurrentDonorType = 1) AND (#nTempCntFree > 0) AND (#nTempCntFree > #n_CurrentTakerFree) THEN
	                            #n_CurrentTakerLine := #idxLineSearchTaker;
	                            #n_CurrentTakerFree := #nTempCntFree;
	                        END_IF;
	                    END_IF;
	                    
	                    // Linie ist FREE
	                    IF (#nTempCntLoaded = 0) AND (#nTempCntEmpty = 0) THEN
	                        IF (#nTempCntFree > 0) AND (#nTempCntFree > #n_CurrentTakerFree) THEN
	                            #n_CurrentTakerLine := #idxLineSearchTaker;
	                            #n_CurrentTakerFree := #nTempCntFree;
	                        END_IF;
	                    END_IF;
	                END_FOR;
	                
	                // Transition
	                IF #n_CurrentTakerLine > 0 THEN
	                    #n_RearrangeSequence := #c0120_SelectCarrierFromDonor;
	                ELSE
	                    #n_RearrangeSequence := #c0170_NoTakerFound;
	                END_IF;
	            END_REGION
	            
	            
	        #c0120_SelectCarrierFromDonor:
	            REGION c0120_SelectCarrierFromDonor
	                //Carrier im Donor finden (letzte belegte Position)
	                //Action
	                #n_SelectedDonorPos := 0;
	                
	                FOR #idxCarrierDonorFind := 1 TO "cNoPositions" DO
	                    IF #arrudt_Line[#n_CurrentDonorLine].arCarrier[#idxCarrierDonorFind].nBarcode > 0 THEN
	                        IF (#n_CurrentDonorType = 2) AND (#arrudt_Line[#n_CurrentDonorLine].arCarrier[#idxCarrierDonorFind].bStatus.%X1) THEN
	                            #n_SelectedDonorPos := #idxCarrierDonorFind;
	                            EXIT;
	                        ELSIF (#n_CurrentDonorType = 1) AND (NOT #arrudt_Line[#n_CurrentDonorLine].arCarrier[#idxCarrierDonorFind].bStatus.%X1) THEN
	                            #n_SelectedDonorPos := #idxCarrierDonorFind;
	                            EXIT;
	                        END_IF;
	                    END_IF;
	                END_FOR;
	                
	                // Transition
	                IF #n_SelectedDonorPos > 0 THEN
	                    #n_RearrangeSequence := #c0130_FindFreeTakerPos;
	                ELSE
	                    #n_RearrangeSequence := #c0175_NoCarrierFound;
	                END_IF;
	            END_REGION
	            
	            
	        #c0130_FindFreeTakerPos:
	            REGION c0130_FindFreeTakerPos
	                //Erste freie Position im Taker suchen
	                //Action
	                #n_SelectedTakerPos := 0;
	                
	                FOR #idxCarrierTakerFind := 1 TO "cNoPositions" DO
	                    IF #arrudt_Line[#n_CurrentTakerLine].arCarrier[#idxCarrierTakerFind].nBarcode = 0 THEN
	                        #n_SelectedTakerPos := #idxCarrierTakerFind;
	                        EXIT;
	                    END_IF;
	                END_FOR;
	                
	                // Transition
	                IF #n_SelectedTakerPos > 0 THEN
	                    #n_RearrangeSequence := #c0140_MoveCarrier;
	                ELSE
	                    #n_RearrangeSequence := #c0170_NoTakerFound;
	                END_IF;
	            END_REGION
	            
	            
	        #c0140_MoveCarrier:
	            REGION c0140_MoveCarrier
	                //Carrier von Donor nach Taker verschieben
	                // Action
	                #udt_InOutMFRTRO.UDT_Ctrl.nAmount := 1;
	                #udt_InOutMFRTRO.UDT_Ctrl.nMode := 1;
	                #udt_InOutMFRTRO.UDT_Ctrl.xStart := True;
	                #str_CurrentTask := 'Linie frei machen gestartet';
	                
	                
	                
	                
	                
	                // Transition
	                #n_RearrangeSequence := #c0150_CheckDonorEmpty;
	            END_REGION
	            
	        #c0143_WaitTRODone:
	            REGION c0143_WaitTRODone
	                // Warten bis verschieben beendet
	                //Action
	                
	                
	                //Transition
	                IF #udt_InOutMFRTRO.UDT_State.xReady
	                THEN
	                    #n_RearrangeSequence := #c0145_ResetTRO;
	                END_IF;
	            END_REGION
	            
	        #c0145_ResetTRO:
	            REGION c0145_ResetTRO
	                //TRO zuruecksetzen
	                //Action 
	                #udt_InOutMFRTRO.UDT_Ctrl.nAmount := 1;
	                #udt_InOutMFRTRO.UDT_Ctrl.nMode := 1;
	                #udt_InOutMFRTRO.UDT_Ctrl.xStart := True;
	                
	                // Donor-Slot leeren
	                #arrudt_Line[#n_CurrentDonorLine].arCarrier[#n_SelectedDonorPos].nBarcode := 0;
	                #arrudt_Line[#n_CurrentDonorLine].arCarrier[#n_SelectedDonorPos].nDestination := 0;
	                #arrudt_Line[#n_CurrentDonorLine].arCarrier[#n_SelectedDonorPos].bStatus := 0;
	                
	                #n_RearrangedCounter := #n_RearrangedCounter + 1;
	                
	                //Transition
	                #n_RearrangeSequence := #c0150_CheckDonorEmpty;
	            END_REGION
	            
	            
	        #c0150_CheckDonorEmpty:
	            REGION c0150_CheckDonorEmpty
	                //Prüfen ob Donorlinie jetzt leer ist
	                #n_RemainingDonor := 0;
	                
	                FOR #idxCarrierDonorCheck := 1 TO "cNoPositions" DO
	                    IF #arrudt_Line[#n_CurrentDonorLine].arCarrier[#idxCarrierDonorCheck].nBarcode > 0 THEN
	                        #n_RemainingDonor := #n_RemainingDonor + 1;
	                    END_IF;
	                END_FOR;
	                
	                // Transition
	                IF #n_RemainingDonor = 0 THEN
	                    
	                    #n_RearrangeSequence := #c0160_Done;
	                ELSE
	                    #n_RearrangeSequence := #c0110_SearchTakerLine;  // weitermachen
	                END_IF;
	            END_REGION
	            
	            
	        #c0160_Done:
	            REGION c0160_Done
	                // Fertig
	                #str_CurrentTask := 'Linie frei machen abgeschlossen';
	                #n_RearrangeSequence := #c0010_SearchLine;
	                #n_NextLine := 1;
	            END_REGION
	            
	            // --------------------------------------------------------
	            // Fehlerpfade
	            // --------------------------------------------------------
	        #c0170_NoTakerFound:
	            REGION c0170_NoTakerFound
	                //Action
	                #x_Error := TRUE;
	                #n_ErrorID := 1001;
	                #str_CurrentTask := 'Kein Taker gefunden';
	                //Transition
	                #n_RearrangeSequence := #c0900_Error;
	            END_REGION
	            
	        #c0175_NoCarrierFound:
	            REGION c0175_NoCarrierFound
	                //Action
	                #x_Error := TRUE;
	                #n_ErrorID := 1002;
	                #str_CurrentTask := 'Kein Carrier im Donor gefunden';
	                
	                //Transition
	                #n_RearrangeSequence := #c0900_Error;
	            END_REGION
	            
	            
	            
	        #c0900_Error:
	            REGION c0900_Error
	                //Actions
	                
	                
	                //Transitions
	                IF #x_InQuit
	                THEN
	                    #n_RearrangeSequence := #c0000_Wait;
	                END_IF;
	            END_REGION
	            
	    END_CASE;
	    
	END_REGION
	
	REGION HMI
	    #udt_HMI.udt_Common.n_ErrorBLKID := #n_ErrorBLKID;
	    #udt_HMI.udt_Common.n_ErrorID := #n_ErrorID;
	    #udt_HMI.udt_Common.x_Error := #x_Error;
	    
	    #udt_HMI.udt_BlockRemovalModul.str_State.x_FreigabeBtnRearrenge := NOT #udt_INOUT_Handshake.str_Ctrl.b_Start AND NOT #udt_INOUT_HandshakeEmpty.str_Ctrl.b_Start;
	    #udt_HMI.udt_BlockRemovalModul.str_State.x_FreigabeBtnRemoval := NOT #udt_Handshake_intern.str_State.b_Busy;
	    
	END_REGION
	
	REGION Auslagern
	    
	    CASE #n_StockRemovalSequence OF
	            
	        #c0000_Wait:
	            //Action
	            #n_SearchSortedBlkIDXSave := 1;
	            #n_SearchSortedLineIDXSave := 1;
	            #udt_Handshake_intern.str_State.b_Busy := FALSE;
	            //Transition
	            IF #udt_Handshake_intern.str_Ctrl.b_RemoveRelease AND NOT #udt_INOUT_HandshakeEmpty.str_Ctrl.b_RemoveRelease
	                OR (#udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_StartRemoval AND #udt_HMI.udt_BlockRemovalModul.str_Ctrl.n_RemovalLine > 0)
	            THEN
	                #n_StockRemovalSequence := #c0010_SearchLine;
	            ELSIF  #udt_INOUT_HandshakeEmpty.str_Ctrl.b_RemoveRelease
	            THEN
	                #n_MaxEmptyLine := #str_InOut_EmptyChart.udt_LineLvlBlk.n_LineMAX;
	                #n_StockRemovalSequence := #c0100_SetDestinationEmpty;
	                
	            END_IF;
	            
	        #c0010_SearchLine:
	            //Action
	            #n_SearchSortedBlkIDXSave := 1;
	            #n_SearchSortedLineIDXSave := 1;
	            #udt_Handshake_intern.str_State.b_Busy := True;
	            #udt_Handshake_intern.str_State.b_Done := False;
	            FOR #n_SearchSortedBlkIDX := 1 TO 12 DO
	                
	                FOR #n_SearchSortedLineIDX := 1 TO 21 DO
	                    IF #arrudt_Line[#n_SearchSortedLineIDX].stData.Status.bByte.%X6
	                    THEN
	                        #n_SearchSortedBlkIDXSave := #n_SearchSortedBlkIDX;
	                        #n_SearchSortedLineIDXSave := #n_SearchSortedLineIDX;
	                        EXIT;
	                    END_IF;
	                END_FOR;
	                
	            END_FOR;
	            
	            //Transition
	            IF #arrudt_Line[#n_SearchSortedLineIDXSave].stData.Status.bByte.%X6
	                OR (#udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_StartRemoval AND #udt_HMI.udt_BlockRemovalModul.str_Ctrl.n_RemovalLine >0
	                AND NOT #udt_Handshake_intern.str_Ctrl.b_RemoveRelease
	                AND NOT #udt_INOUT_HandshakeEmpty.str_Ctrl.b_RemoveRelease
	                AND NOT #udt_INOUT_Handshake.str_State.b_Busy )
	            THEN
	                #n_StockRemovalSequence := #c0020_CheckCapacity;
	            END_IF;
	            
	        #c0020_CheckCapacity:
	            //Action
	            #str_CurrentTask := 'Auslagern wird vorbereitet';
	            IF #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_StartRemoval
	                AND NOT #udt_Handshake_intern.str_Ctrl.b_RemoveRelease
	                AND NOT #udt_INOUT_HandshakeEmpty.str_Ctrl.b_RemoveRelease
	                AND NOT #udt_INOUT_Handshake.str_State.b_Busy
	            THEN
	                IF #udt_HMI.udt_BlockRemovalModul.str_Ctrl.n_RemovalLine > 0 AND #udt_HMI.udt_BlockRemovalModul.str_Ctrl.n_RemovalLine < 22
	                THEN
	                    #n_SearchSortedLineIDXSave := #udt_HMI.udt_BlockRemovalModul.str_Ctrl.n_RemovalLine;
	                END_IF;
	            END_IF;
	            //Transition
	            //IF Capacity JAms > Line Count Then
	            #n_StockRemovalSequence := #c0025_SetDestination;
	            
	        #c0025_SetDestination:
	            //Action
	            FOR #n_DestiantionIDX := 1 TO "cNoPositions" DO
	                IF #arrudt_Line[#n_SearchSortedLineIDXSave].arCarrier[#n_DestiantionIDX].nBarcode > 0
	                THEN
	                    IF #arrudt_Line[#n_SearchSortedLineIDXSave].arCarrier[#n_DestiantionIDX].bStatus.%X8
	                    THEN
	                        #arrudt_Line[#n_SearchSortedLineIDXSave].arCarrier[#n_DestiantionIDX].nDestination := #c001_AMR;
	                    ELSIF #arrudt_Line[#n_SearchSortedLineIDXSave].arCarrier[#n_DestiantionIDX].bStatus.%X9
	                    THEN
	                        #arrudt_Line[#n_SearchSortedLineIDXSave].arCarrier[#n_DestiantionIDX].nDestination := #c002_F2H;
	                    END_IF;
	                END_IF;
	                
	            END_FOR;
	            
	            
	            
	            //Transition
	            #n_StockRemovalSequence := #c0030_SetTRO;
	            
	        #c0030_SetTRO:
	            //Action
	            
	            #udt_InOutMFRTRO.UDT_Ctrl.nMode := 3;
	            #udt_InOutMFRTRO.UDT_Ctrl.nLine := #n_SearchSortedLineIDXSave;
	            #udt_InOutMFRTRO.UDT_Ctrl.xStart := True;
	            #str_CurrentTask := 'Auslagern wurde gestartet';
	            //Transition
	            IF #udt_InOutMFRTRO.UDT_State.xBusy
	            THEN
	                #n_StockRemovalSequence := #c0040_WaitTRODone;
	            END_IF;
	            
	            
	        #c0040_WaitTRODone:
	            //Action
	            //
	            #str_CurrentTask := 'Auslagern wurde gestartet';
	            //Transition
	            IF #udt_InOutMFRTRO.UDT_State.xReady OR #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_Cancel
	            THEN
	                #n_StockRemovalSequence := #c0050_ResetTRO;
	            END_IF;
	            
	        #c0050_ResetTRO:
	            //Action
	            #udt_InOutMFRTRO.UDT_Ctrl.xStart := False;
	            #udt_InOutMFRTRO.UDT_Ctrl.nMode := 0;
	            #arrudt_Line[#n_SearchSortedLineIDXSave].stData.Status.bByte.%X6 := False;
	            #str_CurrentTask := 'Auslagern beendet';
	            #udt_HMI.udt_BlockRemovalModul.str_Ctrl.n_RemovalLine := 0;
	            #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_Cancel := False;
	            #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_StartRearrange := False;
	            #udt_HMI.udt_BlockRemovalModul.str_Ctrl.x_StartRemoval := False;
	            #udt_Handshake_intern.str_State.b_Done := True;
	            //Transiton
	            IF NOT #udt_Handshake_intern.str_Ctrl.b_RemoveRelease
	            THEN
	                #n_StockRemovalSequence := #c0000_Wait;
	            END_IF;
	        #c0100_SetDestinationEmpty:
	            //Action 
	            #udt_INOUT_HandshakeEmpty.str_State.b_Busy := True;
	            #udt_INOUT_HandshakeEmpty.str_State.b_Done := False;
	            FOR #n_DestiantionIDX := 1 TO "cNoPositions" DO
	                IF #n_MaxEmptyLine >= 1 AND #n_MaxEmptyLine <= 21
	                THEN
	                    IF #arrudt_Line[#n_MaxEmptyLine].arCarrier[#n_DestiantionIDX].nBarcode > 0
	                    THEN
	                        IF NOT #arrudt_Line[#n_MaxEmptyLine].arCarrier[#n_DestiantionIDX].bStatus.%X1
	                        THEN
	                            #arrudt_Line[#n_MaxEmptyLine].arCarrier[#n_DestiantionIDX].nDestination := #c003_EmptyCarrier;
	                        ELSE
	                            #arrudt_Line[#n_MaxEmptyLine].arCarrier[#n_DestiantionIDX].nDestination := UINT_TO_INT(#n_InBlkID * 100 + #n_MaxEmptyLine);
	                            
	                        END_IF;
	                    END_IF;
	                END_IF;
	            END_FOR;
	            
	            //Transition
	            #n_StockRemovalSequence := #c0110_SetTRO;
	            
	        #c0110_SetTRO:
	            //Action 
	            #udt_InOutMFRTRO.UDT_Ctrl.nMode := 3;
	            #udt_InOutMFRTRO.UDT_Ctrl.nLine := #n_MaxEmptyLine;
	            #udt_InOutMFRTRO.UDT_Ctrl.xStart := True;
	            #str_CurrentTask := 'Auslagern wurde gestartet';
	            //Transition
	            IF #udt_InOutMFRTRO.UDT_State.xBusy
	            THEN
	                #n_StockRemovalSequence := #c0120_WaitTRODone;
	            END_IF;
	            
	        #c0120_WaitTRODone:
	            //Action
	            //
	            //Transition
	            IF #udt_InOutMFRTRO.UDT_State.xReady
	            THEN
	                #n_StockRemovalSequence := #c0120_WaitTRODone;
	            END_IF;
	            
	        #c0130_ResetTRO:
	            //Action
	            #udt_InOutMFRTRO.UDT_Ctrl.xStart := FALSE;
	            #udt_InOutMFRTRO.UDT_Ctrl.nMode := 0;
	            #udt_InOutMFRTRO.UDT_Ctrl.nLine := 0;
	            #udt_INOUT_HandshakeEmpty.str_State.b_Busy := False;
	            #udt_INOUT_HandshakeEmpty.str_State.b_Done := True;
	            #str_CurrentTask := 'Auslagern wurde beendet';
	            
	            //Transition
	            IF NOT #udt_INOUT_HandshakeEmpty.str_Ctrl.b_Start
	            THEN
	                #n_StockRemovalSequence := #c0000_Wait;
	            END_IF;
	            
	            
	    END_CASE;
	END_REGION
	
	REGION Errors
	    //Quit setzt Error Word zurück
	    IF #x_InQuit
	    THEN
	        #x_Error := False;
	    END_IF;
	    
	    IF #x_Error AND #n_ErrorID = #c0010_NoLineFound
	    THEN
	        #wsErrorMessage := WSTRING#'Keine Linie zum Sortieren gefunden';
	        
	    END_IF;
	    
	    #PrgAlarm_Failmessage(SIG := #x_Error,
	                          TIMESTAMP := #tTempLocalTime,
	                          SD_1 := #sIn_ObjectName,
	                          SD_2 := #wsErrorMessage);
	    
	    
	    
	    
	    
	END_REGION
	
	REGION Send Message To MES
	    
	    #lfb_MessageToMES(x_InSendMessage:=#str_MessageUpdate.str_Ctrl.x_SendMessage,
	                      arrstr_InOutMessageData:=#str_MessageUpdate.str_Ctrl.arrstr_MessageData,
	                      x_OutReady=>#str_MessageUpdate.str_State.x_Ready,
	                      x_OutBusy=>#str_MessageUpdate.str_State.x_Busy,
	                      x_OutDone=>#str_MessageUpdate.str_State.x_Done,
	                      udt_InOutHandshakeMES:=#udt_InOutHandshakeMessageToMes);
	    
	    
	    
	    
	    
	END_REGION
	
	REGION HMI Mapping
	    
	    //#udt_HMI.str_TRO.udt_MFR_TRO := #udt_InOutMFRTRO;
	    #udt_HMI.udt_Common.string_CurrentTask := #str_CurrentTask;
	    
	END_REGION
	
	
	    
END_FUNCTION_BLOCK

