FUNCTION_BLOCK "FB_ILS_MTRO_1Sep2Swi"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      stInSettings : Struct
         nTroStopper1 : Int;
         nTroSwitchExit2 : Int;
         nTroSwitchExit3 : Int;
         xCar2NecRight : Bool;   // 2. Kreisel ist notwendig für Richtung rechts
         xCar2NecLeft : Bool;   // 2. Kreisel ist notwendig für Richtung links
         xPrioDir1 : Bool;   // Für diese Direction ist Prio Funktion relevant
         xPrioDir2 : Bool;   // Für diese Direction ist Prio Funktion relevant
         bSepType : Byte := 16#0;   // X0: TRUE - erstes Bein halten, X1: TRUE- Beladekontrolle, X2: TRUE - Beladeerfassung
         tPartSenHigh : Time := T#500ms;
         tInSenHigh : Time := T#500ms;
         xIsBorner : Bool;   // Hiermit  werden Dummys mit Barcode 1000-2000 angelegt
         nBornerDest : Int;   // Standard Destination festlegen für Borner (variabel)
         tDummyTimeNormal : Time := T#3s;   // Zeit für Dummy anlegen (Dummy = 1)
         tDummyTimeBorner : Time := T#0ms;   // Zeit für Dummy anlegen (Dummy = 1000-2000) schneller...
      END_STRUCT;
      stInSeparator1 : Struct
         stSenInSep : "UDT_Sensor";   // Sensor in Stopper
         stSenSepLeft : "UDT_Sensor";   // Sensor nach Stopper
         stSenPart : "UDT_Sensor";   // Checkt, ob der Bügelträger beladen ist
         stSenStartScan : "UDT_Sensor";   // Sensor nach Stopper wenn Scanner vorhanden. Vermutlich startet dieser Sensor den Scanner
         Settings : "SettingsSeparator";
      END_STRUCT;
      stInSwitch1 : Struct
         stSenExit3 : "UDT_Sensor";
         stSenExit4 : "UDT_Sensor";
         Settings : "SettingsSwitch";
      END_STRUCT;
      stInSwitch2 : Struct
         stSenExit3 : "UDT_Sensor";
         stSenExit4 : "UDT_Sensor";
         Settings : "SettingsSwitch";
      END_STRUCT;
      stInBarcodeReader : Struct
         Settings : "UDT_SettingsBarcodeReader";
      END_STRUCT;
      stInPriorityManager : "UDT_Response";   // Schnittstelle Prioritätenmanager (z.B. bei Kreuzungen)
      stInSenJam2 : "UDT_Sensor";   // Stausensor Exit 2
      stInSenJam3 : "UDT_Sensor";   // Stausensor Exit 3
      stInSenJam4 : "UDT_Sensor";
      nInMainTroNo : UInt;
      xInSftyOk : Bool;
      xInAllRdyToStart : Bool;
      xInJamAtExit2 : Bool;   // NIU Stau Exit 2
      xInJamAtExit3 : Bool;   // NIU Stau Exit 3
      xInCarouselRun1 : Bool;   // Rückmeldesignal von Carousel1: Motor läuft
      xInCarouselRun2 : Bool;   // Rückmeldesignal von Carousel2: Motor läuft
      xInTestMode : Bool;
      xInCancel : Bool;
      xInAreaStopActive : Bool;   // Bereichshalt
      xInRelease : Bool;   // Freigabe/Anforderung
      nInDirection : USInt;
   END_VAR

   VAR_OUTPUT 
      xOutStopper1 : Bool;
      xOutSw1EntrTo1 : Bool;   // Weiche auf Eingang 1 stellen
      xOutSw1EntrTo2 : Bool;   // Weiche auf Eingang 2 stellen
      xOutSw1ExTo3 : Bool;   // Weiche auf Ausgang 3 stellen
      xOutSw1ExTo4 : Bool;   // Weiche auf Ausgang 4 stellen
      xOutSw2EntrTo1 : Bool;   // Weiche auf Eingang 1 stellen
      xOutSw2EntrTo2 : Bool;   // Weiche auf Eingang 2 stellen
      xOutSw2ExTo3 : Bool;   // Weiche auf Ausgang 3 stellen
      xOutSw2ExTo4 : Bool;   // Weiche auf Ausgang 4 stellen
      xOutJamAtEntry1 : Bool;
      xOutCarouselReq1 : Bool;   // Motor 1 anfordern
      xOutCarouselReq2 : Bool;   // Motor 2 anfordern
      xOutCorrection : Bool;
      nOutStateLast : Int;
      stOutPriorityManager : "UDT_Request";
      nOutScanResult : DInt;
      xOutNewScan : Bool;
   END_VAR

   VAR_IN_OUT 
      stInOutMachineState : "UDT_MainState";
      stInOutCognexInterface : "stCognexInterface";
      arInOutJamEntr1 : Array[*] of "stCarrier";   // Daten Staubereich Eingang 1
      stInOutJamEntr1 : "stJamData";
      arInOutJamExit2 : Array[*] of "stCarrier";   // Daten Staubereich Ausgang 2
      stInOutJamExit2 : "stJamData";
      arInOutJamExit3 : Array[*] of "stCarrier";   // Daten Staubereich Ausgang 3
      stInOutJamExit3 : "stJamData";
      arInOutJamExit4 : Array[*] of "stCarrier";   // Daten Staubereich Ausgang 3
      stInOutJamExit4 : "stJamData";
      stInOutHMI : "UDT_HMI_TRO";
   END_VAR

   VAR 
      fbStateManager : "FB_StateManager";
      fbSeparator1 : "FB_ILS_STRO_Sep";
      fbSwitch1 : "FB_ILS_STRO_Switch";
      fbSwitch2 : "FB_ILS_STRO_Switch";
      fbBarcodeReader1 : "FB_BarcodeReaderCognex";   // Instanz Barcode-Reader
   END_VAR
   VAR RETAIN
      stActRequest : Struct
         xNBT : Bool;   // Es handelt sich um einen Nullbügelträger
         xCancelled : Bool;   // Abbruchanforderung durch MFR
         xDirectionChange : Bool;
         nActReqIndex : USInt;   // 0 = nichts zu tun | 1-200: Aktuell bearbeiteter Index
         nActArrIndex : USInt;
         nDirection : USInt;   // 0: Es bleibt hier alles so wie es ist! | 1: E1->A3 | 2: E1->A4 | 3: E2->A3 | 4: E2->A4
         nLastDirection : USInt;
         nRequestedNoBT : USInt;   // Anzahl Trolleys die ausgefahren werden sollen
         nReasonForStop : USInt;   // 0: Nichts | 5: Alle BT ausgefahren | 10: Fehler in SubTro | 15: Kein BT/NBT am Eingang | 20: Stau im nächsten MTRO | 25: NBT | 30: Falsch gefahren | 35: Initial-BT kam nicht am Ziel an | 40: Allgemeiner Fehler | 45: Abbruch durch MFR | 50: Fehler in Anfrage von MRF
         nActNoStopper : USInt;   // Anzahl der Stopper-öffnungen in diesem Auftrag
         nActNoInSwitch : USInt;   // Anzahl BT die sich noch in Weiche befinden müssten
         nActNoInSwitchLast : USInt;
         xLongSignal : Bool;
      END_STRUCT;
      nState : Int;
   END_VAR
   VAR 
      sStateText : String["cStateText"];   // Aktueller State der Schrittkette (textuell)
   END_VAR
   VAR RETAIN
      nSetSwitch1 : USInt;   // 0: Es bleibt hier alles so wie es ist! | 1: E1->A2 | 2: E1->A3
   END_VAR
   VAR 
      nSetSwitch2 : USInt;
   END_VAR
   VAR RETAIN
      xReleaseTrolleySep1 : Bool;   // Öffne-Befehl
      xErrorMotor : Bool;   // Fehler weil Motor-Run fehlt
      xMTRORdy : Bool;   // MainTRO ist bereit für neue Aufträge (Signal an JobManager)
      xWrongDirDetect : Bool;   // Wrong Dir wurde erkannt
   END_VAR
   VAR 
      tOnSvReleaseSep {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Supervision - Freigabe Separator
      xStartScan : Bool;
      xHandshakeScan : Bool;
      nBarcodeResult : DInt;
      nBarcodeHelp : DInt;
      sBarcodeString : String;
      xCodeEven : Bool;
      xTrolleyWithPart : Bool;
      xTrolleyWoPart : Bool;
      xReadSecondLeg : Bool;
      wErrorWord : Word;
      nLastDirection : USInt;
      tOnSwitchDelay {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
   END_VAR

   VAR_TEMP 
      nIndex : USInt;
      tmpActUnivTon : Bool;
      tmpReqFinished : Bool;   // Temp Signal für Switch
      xBtEmpty : Bool;
      xCarouselRun : Bool;
      tmpReasonForStop : USInt;   // Anhaltegrund in der Schrittkette | 0: Nichts | 5: Alle BT ausgefahren | 10: Fehler in SubTro | 15: Kein BT/NBT am Eingang | 20: Stau im nächsten MTRO | 25: NBT | 30: Falsch gefahren | 35: Initial-BT kam nicht am Ziel an | 40: Allgemeiner Fehler | 45: Abbruch durch MFR | 50: Fehler in Anfrage von MRF | 55: MFR hat TRO pausiert
      stEmptyCarrier : "stCarrier";
      stBookingInfo : "stEventBookingInfo";
      arEmptyCarrier : Array[1..1] of "stCarrier";   // Platzhalter für CarrierArray
      xGoodRead : Bool;
      stEventGoodRead : "stEventScanInfo";
      arEventCorrectionInfo : Array[0..1] of "stEventCorrectionInfo";
      stEventNew : "stEventScanInfo";
      stEventUnknown : "stEventScanInfo";
      xTemp : Bool;
      xNewCarrier : Bool;
      xUnknownCarrier : Bool;
      xTmpCorrection : Bool;
      xStartSwitchDelay : Bool;
   END_VAR

   VAR CONSTANT 
      "1SEP1SWITCH" : USInt := 3;
      c0000_StepOff : Int := 0;   // Schrittkette aus
      c0100_StepUndefined : Int := 100;   // Schrittkette undefiniert / Init
      c1000_StepWaitForTrolley1 : Int := 1000;   // Warte auf Trolley 1
      c1100_StepScannerTrig1 : Int := 1100;   // Scanner Triggern 
      c1200_StepScannerEval1 : Int := 1200;   // Scanergebnis auswerten 
      c1300_StepCheckLoad : Int := 1300;   // Beladung prüfen
      c1400_StepCheckSep1 : Int := 1400;   // Separator checken
      c1500_StepReleaseSep1 : Int := 1500;   // Freigabe an Separator
      c2000_StepWaitForTrolley2 : Int := 2000;   // Warte auf Trolley 2
      c2100_StepScannerTrig2 : Int := 2100;   // Scanner Triggern (nur wenn 1.Achse kein Ergebnis)
      c2200_StepScannerEval2 : Int := 2200;   // Scanergebnis auswerten (nur wenn 1.Achse kein Ergebnis)
      c2250_StepCheckDir : Int := 2250;
      c2300_StepCheckSep2 : Int := 2300;   // Separator checken
      c2350_StepCheckSwi : Int := 2350;
      c2400_StepCheckJam : Int := 2400;   // Stau überprüfen
      c2500_StepReleaseSep2 : Int := 2500;   // Freigabe an Separator
      cCarrierDummy : Int := 1;
      cCarrierUnknown : Int := 2;
   END_VAR


BEGIN
	(* Company: Schönenberger Systeme GmbH / (C) Copyright (2025). All rights reserved.
	-----------------------------------------------------------------------------------------
	Title: FB_MTRO_1Sep1Swi
	Comment/Function: MainTRO-Function block for controlling TRO with 1x Sep and 1x Switch 
	Library/Family: "MainTRO"
	Author: department: "Automatisierung" ; person in charge / contact: E.Yüksel, T. Molina, T.Schiller
	Target System: CPU 1518F-4 PN/DP
	Engineering: TIA Portal V20
	Restrictions: -
	Requirements: (hardware, technological package, etc.)
	-----------------------------------------------------------------------------------------
	Change log table:
	Version     | Date       | Signature (Safety) | Expert in charge | Changes applied
	------------|------------|--------------------|------------------|-----------------------
	001.000.000 | 2022-12-15 | -                  | Yüksel, Molina   | Header added on 15.12.2022
	002.000.000 | 2025-02-11 | -                  | Yüksel           | Adapt to ILS with Minitrolley, without external MFR
	                                                                   
	*)
	
	REGION Zweiten Motor einbinden
	    IF #stInSettings.xCar2NecLeft AND
	        (#stActRequest.nDirection = 1 OR #stActRequest.nDirection = 3) THEN
	        #xCarouselRun := #xInCarouselRun1 AND #xInCarouselRun2; //Nach links wird 2. Motor gebraucht und aktuelle Richtung ist LINKS
	    ELSIF #stInSettings.xCar2NecLeft AND
	        (#stActRequest.nDirection = 2 OR #stActRequest.nDirection = 4) THEN
	        #xCarouselRun := #xInCarouselRun1; //Nach links wird 2. Motor gebraucht und aktuelle Richtung ist RECHTS
	    ELSIF #stInSettings.xCar2NecRight AND
	        (#stActRequest.nDirection = 1 OR #stActRequest.nDirection = 3) THEN
	        #xCarouselRun := #xInCarouselRun1; //Nach rechts wird 2. Motor gebraucht und aktuelle Richtung ist LINKS
	    ELSIF #stInSettings.xCar2NecRight AND
	        (#stActRequest.nDirection = 2 OR #stActRequest.nDirection = 4) THEN
	        #xCarouselRun := #xInCarouselRun1 AND #xInCarouselRun2; //Nach rechts wird 2. Motor gebraucht und aktuelle Richtung ist RECHTS
	    ELSE
	        #xCarouselRun := #xInCarouselRun1; //normal
	    END_IF;
	END_REGION
	
	REGION Schrittkette
	    
	    CASE #nState OF
	            
	        #c0000_StepOff:
	            REGION 0000: Off
	                //akt. Zustand
	                #sStateText := 'OFF';
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                #nSetSwitch1 := 0;
	                #nSetSwitch2 := 0;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                
	            END_REGION
	            
	        #c0100_StepUndefined:
	            
	            REGION 0100: Undefined
	                // akt. Zustand
	                #sStateText := 'Undefined';
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                #nSetSwitch1 := 0;
	                #nSetSwitch2 := 0;
	                #xMTRORdy := FALSE;
	                #xCodeEven := FALSE;
	                #xOutNewScan := FALSE;
	                
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                
	                #nState := #c1000_StepWaitForTrolley1;
	                
	            END_REGION
	            
	        #c1000_StepWaitForTrolley1:
	            REGION 1000: Wait for Trolley
	                //akt. Zustand
	                #sStateText := 'Wait for Trolley';
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                
	                #xReleaseTrolleySep1 := FALSE;
	                #xCodeEven := FALSE;
	                #xOutNewScan := FALSE;
	                #xMTRORdy := TRUE;
	                
	                //Transition
	                IF NOT #stInOutHMI.UDT_Ctrl.xPause THEN
	                    IF #fbSeparator1.stOutMainTro.xBtPresent THEN //Separator meldet Belegung
	                        IF #stInSeparator1.Settings.xPluggedScanner THEN //Scanner verbaut?
	                            #nState := #c1100_StepScannerTrig1; //Nächster Schritt -> Scannen
	                        ELSIF #arInOutJamEntr1[1].bStatus.%X0 THEN // Kein Scanner verbaut, aber Carrier online (z.B. Dummy wurde angelegt)
	                            IF #stInSeparator1.Settings.xPluggedPart THEN
	                                #nState := #c1300_StepCheckLoad; // Nächster Schritt -> Beladung prüfen
	                            ELSE
	                                #nState := #c1400_StepCheckSep1; //Nächster Schritt -> Separator chekcen
	                            END_IF;
	                        END_IF;
	                    END_IF;
	                END_IF;
	                
	            END_REGION
	            
	        #c1100_StepScannerTrig1:
	            REGION 1100: Scanner Trigger 1
	                
	                // akt. Zustand
	                #sStateText := 'Scanner Trigger 1';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                IF #fbBarcodeReader1.xOutReadyForNewScan THEN //Scanner bereit
	                    #xStartScan := TRUE; //Startbefehl 
	                END_IF;
	                
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                
	                IF #fbBarcodeReader1.xOutReadingDone THEN //Scanner Rückmeldung
	                    #xStartScan := FALSE;
	                    #nState := #c1200_StepScannerEval1; //Weiter
	                END_IF;
	            END_REGION
	            
	        #c1200_StepScannerEval1:
	            REGION 1200: Scanner Evaluation 1
	                
	                // akt. Zustand
	                #sStateText := 'Scanner Evaluation 1';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                // Logic - TODO in eigenen FC packen?..
	                
	                IF #fbBarcodeReader1.xOutCodeGood THEN
	                    #nBarcodeResult := LINT_TO_DINT(#fbBarcodeReader1.#nOutBarcode); // Good read vom Barcode reader übernehmen
	                    
	                    REGION Event log Good read
	                        #xGoodRead := TRUE; // Trigger
	                        #stEventGoodRead.nScanType := 4;
	                        #stEventGoodRead.sBarcode := DELETE(IN := DINT_TO_STRING(#nBarcodeResult), L := 1, P := 1);
	                        #stEventGoodRead.sReaderId := DELETE(IN := INT_TO_STRING(#stInBarcodeReader.Settings.#nStation), L := 1, P := 1);
	                    END_REGION
	                    
	                    REGION Gerade Zahl erkannt
	                        IF (((#nBarcodeResult / 10) MOD 100) MOD 2 <> 1) THEN //anderes Ergebnis als 1 bedeutet, (xxxxxYYx) YY=gerade, also hinteres Bein von MiniTrolley
	                            #xCodeEven := TRUE; //Bit sagt Separator dass hinteres Bein und nicht erstes zur Korrektur in Schrittkette
	                            // #nBarcodeTarget := #nBarcodeTarget - 10; //Mache danach die gerade Zahl zur ungeraden UPDATE: WEGEN 8. ZIFFER KLAPPT DAS SO NICHT
	                            #nBarcodeHelp := (#nBarcodeResult / 10) - 1;
	                            
	                            "FC_Ean8_CheckDigit"(sInDigits := DELETE(IN := DINT_TO_STRING(IN := #nBarcodeHelp), L := 1, P := 1),
	                                                 sOutBarcode => #sBarcodeString,
	                                                 nOutBarcode => #nBarcodeResult);
	                            
	                            //    #sOutBarcode := #sBarcodeString;
	                        END_IF;
	                    END_REGION
	                    
	                ELSIF #fbBarcodeReader1.#xOutCodeBad THEN
	                    #nBarcodeResult := #arInOutJamEntr1[1].nBarcode;  //erwarteter Barcode wird aus JamDaten übernommen
	                END_IF;
	                
	                IF (#nBarcodeResult > 100) AND
	                    (#nBarcodeResult < 2147483647) AND  // Good Read (2.147.483.647 Maximum von DINT)
	                    (#arInOutJamEntr1[1].nBarcode = 0 OR #arInOutJamEntr1[1].nBarcode = 1) THEN    //Wenn keine Daten oder ein Dummy erwartet werden   
	                    
	                    #xTmpCorrection := TRUE;
	                    // REGION Carr_Glob_Correction
	                    
	                    
	                    //     "FC_CARR_GLOB_CORRECTION"(xInStart := TRUE, //Führe Korrektur aus
	                    //                               nInBarcodeScan := #nBarcodeResult,
	                    //                               nInStation := #stInBarcodeReader.Settings.nStation,
	                    //                               xOutNotFound => #xTemp,
	                    //                               arOutEventLogInfo => #arEventCorrectionInfo,
	                    //                               InOutCarrierExpect := #arInOutJamEntr1[1]);
	                    // END_REGION
	                    
	                    
	                    // IF NOT #xTemp THEN
	                    //     ;
	                    //     // #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    // ELSE
	                    //     #arInOutJamEntr1[1].nDestination := "cDestNONE"; //neuer Carrier // TODO
	                    //     //     #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    //     #arInOutJamEntr1[1].nBarcode := #nBarcodeResult; //Hier werden neue Carrier im System angelegt
	                    //     #arInOutJamEntr1[1].bStatus.%X0 := TRUE;
	                    // END_IF;
	                    
	                    // REGION Event Log New Carrier
	                    
	                    //     #xNewCarrier := TRUE; // Trigger
	                    //     #stEventNew.nScanType := 2;
	                    //     #stEventNew.sBarcode := DELETE(IN := DINT_TO_STRING(#arInOutJamEntr1[1].nBarcode), L := 1, P := 1);
	                    //     #stEventNew.sDest := DELETE(IN := INT_TO_STRING(#arInOutJamEntr1[1].nDestination), L := 1, P := 1);
	                    //     #stEventNew.sReaderId := DELETE(IN := INT_TO_STRING(#stInBarcodeReader.Settings.#nStation), L := 1, P := 1);
	                    
	                    // END_REGION
	                    
	                ELSIF
	                    (#nBarcodeResult > 100) AND
	                    (#nBarcodeResult < 2147483647) AND // Good Read (2.147.483.647 Maximum von DINT)
	                    #arInOutJamEntr1[1].nBarcode = #nBarcodeResult THEN //Barcodes stimmen überein ODER es wird hier gelandet mit BadRead wobei normaler Barcode erwartet wird-> mache normal weiter
	                    ;
	                ELSIF
	                    (#nBarcodeResult > 100) AND
	                    (#nBarcodeResult < 2147483647) AND
	                    #arInOutJamEntr1[1].nBarcode <> #nBarcodeResult AND //Barcodes stimmen nicht überein, erwarteter Carrier enthält aber normalen Barcode
	                    #arInOutJamEntr1[1].nBarcode <> 0 AND
	                    #arInOutJamEntr1[1].nBarcode <> 1 THEN
	                    
	                    #xTmpCorrection := TRUE;
	                    
	                    // REGION Carr_Glob_Correction
	                    
	                    //     "FC_CARR_GLOB_CORRECTION"(xInStart := TRUE, //Führe Korrektur aus
	                    //                               nInBarcodeScan := #nBarcodeResult,
	                    //                               nInStation := #stInBarcodeReader.Settings.nStation,
	                    //                               xOutNotFound => #xTemp,
	                    //                               arOutEventLogInfo => #arEventCorrectionInfo,
	                    //                               InOutCarrierExpect := #arInOutJamEntr1[1]);
	                    // END_REGION
	                    
	                    // IF NOT #xTemp THEN
	                    //     ;
	                    //     // #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    // ELSE
	                    //     #arInOutJamEntr1[1].nDestination := "cDestNONE"; //neuer Carrier // TODO
	                    //     //     #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    //     #arInOutJamEntr1[1].nBarcode := #nBarcodeResult; //Hier werden neue Carrier im System angelegt
	                    //     #arInOutJamEntr1[1].bStatus.%X0 := TRUE;
	                    // END_IF;
	                    
	                    // REGION Event Log New Carrier
	                    
	                    //     #xNewCarrier := TRUE; // Trigger
	                    //     #stEventNew.nScanType := 2;
	                    //     #stEventNew.sBarcode := DELETE(IN := DINT_TO_STRING(#arInOutJamEntr1[1].nBarcode), L := 1, P := 1);
	                    //     #stEventNew.sDest := DELETE(IN := INT_TO_STRING(#arInOutJamEntr1[1].nDestination), L := 1, P := 1);
	                    //     #stEventNew.sReaderId := DELETE(IN := INT_TO_STRING(#stInBarcodeReader.Settings.#nStation), L := 1, P := 1);
	                    
	                    // END_REGION
	                    
	                    
	                ELSE        // Bad Read, falls nichts oder Dummy erwartet wird -> HANDLING TO DO!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	                    #arInOutJamEntr1[1].bStatus.%X0 := TRUE;
	                    #arInOutJamEntr1[1].nBarcode := 2; //unbekannter Carrier mit Badread, erwartet 0 und physikalisch vorhanden
	                    #arInOutJamEntr1[1].nDestination := 123; //todo
	                    
	                    REGION Event Log unknown Carrier
	                        
	                        #xUnknownCarrier := TRUE; // Trigger
	                        #stEventUnknown.nScanType := 3;
	                        #stEventUnknown.sBarcode := DELETE(IN := DINT_TO_STRING(#arInOutJamEntr1[1].nBarcode), L := 1, P := 1);
	                        #stEventUnknown.sDest := DELETE(IN := INT_TO_STRING(#arInOutJamEntr1[1].nDestination), L := 1, P := 1);
	                        #stEventUnknown.sReaderId := DELETE(IN := INT_TO_STRING(#stInBarcodeReader.Settings.#nStation + 50), L := 1, P := 1);
	                        
	                    END_REGION
	                    
	                END_IF;
	                
	                #nOutScanResult := #nBarcodeResult;
	                #xOutNewScan := TRUE;
	                
	                #xHandshakeScan := TRUE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                IF #stInSeparator1.Settings.xPluggedPart AND #stInSettings.bSepType.%X2 THEN
	                    #nState := #c1300_StepCheckLoad;
	                ELSIF #xCodeEven THEN
	                    #xCodeEven := FALSE;
	                    #nState := #c2000_StepWaitForTrolley2; //Scanner bestätigt zweites Bein gelesen -> Korrigieren!
	                ELSE
	                    #nState := #c1400_StepCheckSep1;
	                END_IF;;
	                
	            END_REGION
	            
	        #c1300_StepCheckLoad:
	            REGION 1300: Check Load 
	                
	                // akt. Zustand
	                #sStateText := 'Check Load';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xHandshakeScan := FALSE;
	                #xReleaseTrolleySep1 := FALSE;
	                #xOutNewScan := FALSE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                IF #fbStateManager.tOutTimeInStep > #stInSettings.tPartSenHigh THEN //Verzögern wegen Auswertung
	                    IF #xTrolleyWithPart THEN
	                        #arInOutJamEntr1[1].bStatus.%X1 := TRUE;
	                        #nState := #c1400_StepCheckSep1; // Weiter
	                    ELSIF #xTrolleyWoPart THEN
	                        #arInOutJamEntr1[1].bStatus.%X1 := FALSE;
	                        #nState := #c1400_StepCheckSep1; // Weiter
	                    END_IF;
	                END_IF;
	                
	            END_REGION
	            
	        #c1400_StepCheckSep1:
	            REGION 1400: Check Separator                
	                // akt. Zustand
	                #sStateText := 'Check Sep';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                #xOutNewScan := FALSE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                #tmpActUnivTon := TRUE;
	                IF #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForReq" THEN // Separator ist bereit
	                    #nState := #c1500_StepReleaseSep1;
	                ELSIF #fbStateManager.tOutUnivTonR > T#3s THEN
	                    #tmpReasonForStop := "Rsn_SubError";    // SubTRO: Separator ist nicht bereit
	                END_IF;
	                
	            END_REGION
	            
	            
	        #c1500_StepReleaseSep1:
	            REGION 1500: Release Sep 1            
	                // akt. Zustand
	                #sStateText := 'Leave Sep 1';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                
	             //   #xReleaseTrolleySep1 := #xCarouselRun AND #xInSftyOk; // Erste Achse darf vereinzelt werden
	             
	                #xReleaseTrolleySep1 := (#xCarouselRun AND #xInSftyOk) AND
	                (NOT #stInSettings.bSepType.%X0 OR
	                (#xInRelease AND #stInSettings.bSepType.%X0 AND NOT #stInSettings.bSepType.%X1) OR
	                (#xInRelease AND #stInSettings.bSepType.%X0 AND #stInSettings.bSepType.%X1 AND
	                ((#arInOutJamEntr1[1].bStatus.%X1 AND #xTrolleyWithPart) OR
	                (NOT #arInOutJamEntr1[1].bStatus.%X1 AND #xTrolleyWoPart)))); // Erste Achse darf vereinzelt werden
	             
	                //////// Transition ////////    
	                IF #fbSeparator1.stOutMainTro.nStatus = "SepState_Finish" THEN
	                    #nState := #c2000_StepWaitForTrolley2;
	                ELSIF #xReleaseTrolleySep1 AND #tOnSvReleaseSep.Q THEN
	                    #tmpReasonForStop := "Rsn_SubError";    //  Timeout-Fehler im SubTRO "Separator": Freigabe erteilt, aber Separator gibt keine Rückmeldung an MainTRO
	                END_IF;
	                
	            END_REGION
	            
	        #c2000_StepWaitForTrolley2:
	            REGION 2000: Wait for Trolley 2
	                //akt. Zustand
	                #sStateText := 'Wait for Trolley 2';
	                
	                
	                #xTrolleyWithPart := FALSE;
	                #xOutNewScan := FALSE;
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                
	                #xReleaseTrolleySep1 := FALSE;
	                
	                //Korrektur 2.Bein kommt nicht
	                (*  IF NOT #fbSeparator1.stOutMainTro.xBtPresent (* AND #xInCarouselRun *)AND NOT #stInSeparator1.Settings.xPluggedScanner AND #fbStateManager.tOutTimeInStep > T#3s THEN
	                    #nState := #c1000_StepWaitForTrolley1;
	                END_IF; *)
	                
	                
	                //Transition
	                IF NOT #stInOutHMI.UDT_Ctrl.xPause THEN
	                    IF #fbSeparator1.stOutMainTro.xBtPresent AND //Separator meldet Belegung
	                        #arInOutJamEntr1[1].bStatus.%X0 // Carrier ist online
	                        AND ((#xInRelease AND NOT #stInSettings.bSepType.%X0) OR #stInSettings.bSepType.%X0)//Freigabe
	                    THEN
	                        IF #xReadSecondLeg // zweites Bein muss gelesen werden (TODO!!!!)
	                            AND #stInSeparator1.Settings.xPluggedScanner // Scanner verbaut?
	                        THEN
	                            #nState := #c2100_StepScannerTrig2; // Nächster Schritt -> Scannen
	                        ELSE
	                            #nState := 2225;//#c2250_StepCheckDir; // Nächster Schritt -> Check Direction
	                        END_IF;
	                    END_IF;
	                END_IF;
	            END_REGION
	            
	        #c2100_StepScannerTrig2:
	            REGION 2100: Scanner Trigger 2
	                
	                // akt. Zustand
	                #sStateText := 'Scanner Trigger 2';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                IF #fbBarcodeReader1.xOutReadyForNewScan THEN
	                    #xStartScan := TRUE;
	                END_IF;
	                
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                
	                IF #fbBarcodeReader1.xOutReadingDone THEN
	                    #xStartScan := FALSE;
	                    #nState := #c2200_StepScannerEval2;
	                END_IF;
	            END_REGION
	            
	        #c2200_StepScannerEval2:
	            REGION 2200: Scanner Evaluation 2
	                
	                // akt. Zustand
	                #sStateText := 'Scanner Evaluation 2';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                // Logic
	                
	                IF #fbBarcodeReader1.xOutCodeGood THEN
	                    #nBarcodeResult := LINT_TO_DINT(#fbBarcodeReader1.#nOutBarcode); // Good read vom Barcode reader
	                ELSIF #fbBarcodeReader1.#xOutCodeBad THEN
	                    #nBarcodeResult := #arInOutJamEntr1[1].nBarcode;  //erwarteter Barcode wird aus JamDaten übernommen
	                END_IF;
	                
	                IF (#nBarcodeResult > 100) AND
	                    (#nBarcodeResult < 2147483647) AND  // Good Read (2.147.483.647 Maximum von DINT)
	                    (#arInOutJamEntr1[1].nBarcode = 0 OR #arInOutJamEntr1[1].nBarcode = 1) THEN    //Wenn keine Daten oder ein Dummy erwartet werden   
	                    REGION TODO
	                        (*  "FC_CARR_GLOB_CORRECTION"(xInStart := TRUE, //Führe Korrektur aus
	                                                    nInBarcodeScan := #nBarcodeResult,
	                                                    nInStation := 0, // todo
	                                                    xOutNotFound => #xTemp,
	                                                    arOutEventLogInfo => #arEventCorrectionInfo,
	                                                    InOutCarrierExpect := #arInOutJamEntr1[1]); *)
	                    END_REGION
	                    
	                    
	                    IF NOT #xTemp THEN
	                        ;
	                        // #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    ELSE
	                        #arInOutJamEntr1[1].nDestination := "cDestNONE"; //neuer Carrier // TODO
	                        //     #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                        #arInOutJamEntr1[1].nBarcode := #nBarcodeResult; //Hier werden neue Carrier im System angelegt
	                        #arInOutJamEntr1[1].bStatus.%X0 := TRUE;
	                    END_IF;
	                ELSIF
	                    (#nBarcodeResult > 100) AND
	                    (#nBarcodeResult < 2147483647) AND // Good Read (2.147.483.647 Maximum von DINT)
	                    #arInOutJamEntr1[1].nBarcode = #nBarcodeResult THEN //Barcodes stimmen überein ODER es wird hier gelandet mit BadRead wobei normaler Barcode erwartet wird-> mache normal weiter
	                    ;
	                ELSIF
	                    (#nBarcodeResult > 100) AND
	                    (#nBarcodeResult < 2147483647) AND
	                    #arInOutJamEntr1[1].nBarcode <> #nBarcodeResult AND //Barcodes stimmen nicht überein, erwarteter Carrier enthält aber normalen Barcode
	                    #arInOutJamEntr1[1].nBarcode <> 0 AND
	                    #arInOutJamEntr1[1].nBarcode <> 1 THEN
	                    ;
	                    REGION TODO
	                        (*           "FC_CARR_GLOB_CORRECTION"(xInStart := TRUE, //Führe Korrektur aus
	                                                                       nInBarcodeScan := #nBarcodeResult,
	                                                                       nInStation := 0, // todo
	                                                                       xOutNotFound => #xTemp,
	                                                                       arOutEventLogInfo => #arEventCorrectionInfo,
	                                                                       InOutCarrierExpect := #arInOutJamEntr1[1]); *)
	                    END_REGION
	                    
	                ELSE        // Bad Read, falls nichts oder Dummy erwartet wird -> HANDLING TO DO!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	                    #arInOutJamEntr1[1].bStatus.%X0 := TRUE;
	                    #arInOutJamEntr1[1].nBarcode := 2; //unbekannter Carrier mit Badread, erwartet 0 und physikalisch vorhanden
	                    #arInOutJamEntr1[1].nDestination := 123; //todo
	                END_IF;
	                
	                #xHandshakeScan := TRUE;
	                
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                #nState := 2225;//#c2250_StepCheckDir;
	            END_REGION ;
	            
	        2225:
	            REGION 2225: Get Direction
	                // akt. Zustand
	                #sStateText := 'Get Dir';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                #stActRequest.nDirection := #nInDirection;
	                
	                IF #stActRequest.nDirection = 0 THEN
	                    #tmpReasonForStop := "Rsn_MfrMistake";
	                ELSE
	                    #nState := #c2250_StepCheckDir;
	                END_IF;
	            END_REGION
	            
	            
	            
	        #c2250_StepCheckDir:
	            REGION 2250: Check Direction      
	                
	                // akt. Zustand
	                #sStateText := 'Check Dir';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                //Richtungswechsel muss noch berücksichtigt werden!
	                IF #nLastDirection <> #stActRequest.nDirection THEN // Richtungswechsel -> Warten
	                    //  #tmpActUnivTon := TRUE;
	                    #xStartSwitchDelay := true;
	                    IF #tOnSwitchDelay.Q THEN
	                        CASE #stActRequest.nDirection OF
	                            1:
	                                #nSetSwitch1 := 1;
	                                #nSetSwitch2 := 0;
	                            2:
	                                #nSetSwitch1 := 2;
	                                #nSetSwitch2 := 1;
	                            3: //
	                                #nSetSwitch1 := 2;
	                                #nSetSwitch2 := 2;
	                        END_CASE;
	                        #nLastDirection := #stActRequest.nDirection;
	                        #nState := #c2300_StepCheckSep2;
	                    END_IF;
	                ELSIF #nLastDirection = #nInDirection THEN // Gleiche Richtung -> Weiter
	                    CASE #stActRequest.nDirection OF
	                        1:
	                            #nSetSwitch1 := 1;
	                            #nSetSwitch2 := 0;
	                        2:
	                            #nSetSwitch1 := 2;
	                            #nSetSwitch2 := 1;
	                        3:
	                            #nSetSwitch1 := 2;
	                            #nSetSwitch2 := 2;
	                    END_CASE;
	                    #nLastDirection := #stActRequest.nDirection;
	                    #nState := #c2300_StepCheckSep2;
	                END_IF;
	            END_REGION
	            
	            
	        #c2300_StepCheckSep2:
	            REGION 2300: Check Separator                
	                // akt. Zustand
	                #sStateText := 'Check Sep';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xHandshakeScan := FALSE;
	                #xReleaseTrolleySep1 := FALSE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                #tmpActUnivTon := TRUE;
	                IF #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForReq" THEN // Separator ist bereit
	                    #nState := #c2350_StepCheckSwi;
	                ELSIF #fbStateManager.tOutUnivTonR > T#3s THEN
	                    #tmpReasonForStop := "Rsn_SubError";    // SubTRO: Separator ist nicht bereit
	                END_IF;
	                
	            END_REGION
	            
	            
	        #c2350_StepCheckSwi:
	            REGION 2300: Check Switch
	                
	                //akt. Zustand
	                #sStateText := 'Check Switch';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                
	                CASE #stActRequest.nDirection OF
	                    1:
	                        IF NOT #fbSwitch1.stOutMainTro.xStatusIn AND
	                            NOT #fbSwitch1.stOutMainTro.xStatusOut AND NOT #fbSwitch1.xOutPermSigOutSen3 THEN
	                            #nState := #c2400_StepCheckJam;
	                        ELSE
	                            #tmpReasonForStop := "Rsn_SubError"; // Fehler im SubTRO: Weiche
	                        END_IF;
	                    2:
	                        IF NOT #fbSwitch1.stOutMainTro.xStatusIn AND
	                            #fbSwitch1.stOutMainTro.xStatusOut AND
	                            NOT #fbSwitch2.stOutMainTro.xStatusIn AND
	                            NOT #fbSwitch2.stOutMainTro.xStatusOut
	                            AND NOT #fbSwitch1.xOutPermSigOutSen4
	                        THEN
	                            #nState := #c2400_StepCheckJam;
	                        ELSE
	                            #tmpReasonForStop := "Rsn_SubError"; // Fehler im SubTRO: Weiche
	                        END_IF;
	                    3:
	                        IF NOT #fbSwitch1.stOutMainTro.xStatusIn AND
	                            #fbSwitch1.stOutMainTro.xStatusOut AND
	                            NOT #fbSwitch2.stOutMainTro.xStatusIn AND
	                            #fbSwitch2.stOutMainTro.xStatusOut
	                        THEN
	                            #nState := #c2400_StepCheckJam;
	                        ELSE
	                            #tmpReasonForStop := "Rsn_SubError"; // Fehler im SubTRO: Weiche
	                        END_IF;
	                    ELSE
	                        #tmpReasonForStop := "Rsn_GeneralError"; // Allgemeiner Fehler falls Direction irgendetwas anderes ist als 1 oder 2
	                END_CASE;
	            END_REGION
	            
	        #c2400_StepCheckJam:
	            REGION 2400: Check Jam
	                // akt. Zustand
	                #sStateText := 'Check Jam';
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                /////////  Timer  //////////
	                //////// Transition ////////
	                CASE #stActRequest.nDirection OF
	                    1: //Ausgang 2 wird auf Stau geprüft
	                        IF (#stInOutJamExit2.Status.nCount = #stInOutJamExit2.Config.nCapacitySet) OR #stInSenJam2.xJam THEN  //Stau vom nächsten MainTro wird gemeldet
	                            #tmpReasonForStop := "Rsn_JamNextError";
	                        ELSIF #fbSwitch1.stOutMainTro.xJamAtExit3 THEN  //Stau vom SubTro wird gemeldet
	                            #tmpReasonForStop := "Rsn_SubError";
	                        ELSE
	                            #nState := #c2500_StepReleaseSep2;  //ohne Stau nächster Schritt
	                        END_IF;
	                    2: //Ausgang 3 wird auf Stau geprüft
	                        IF (#stInOutJamExit3.Status.nCount = #stInOutJamExit3.Config.nCapacitySet) OR #stInSenJam3.xJam THEN  //Stau vom nächsten MainTro wird gemeldet
	                            #tmpReasonForStop := "Rsn_JamNextError";
	                        ELSIF #fbSwitch1.stOutMainTro.xJamAtExit4 THEN  //Stau vom SubTro wird gemeldet
	                            #tmpReasonForStop := "Rsn_SubError";
	                        ELSE
	                            #nState := #c2500_StepReleaseSep2;   //ohne Stau nächster Schritt
	                        END_IF;
	                    3:
	                        IF (#stInOutJamExit4.Status.nCount = #stInOutJamExit4.Config.nCapacitySet) OR #stInSenJam4.xJam THEN  //Stau vom nächsten MainTro wird gemeldet
	                            #tmpReasonForStop := "Rsn_JamNextError";
	                        ELSIF #fbSwitch1.stOutMainTro.xJamAtExit4 THEN  //Stau vom SubTro wird gemeldet
	                            #tmpReasonForStop := "Rsn_SubError";
	                        ELSE
	                            #nState := #c2500_StepReleaseSep2;   //ohne Stau nächster Schritt
	                        END_IF;
	                END_CASE;
	                
	            END_REGION
	            
	        #c2500_StepReleaseSep2:
	            REGION 2500: Leave, Switch confirmed
	                
	                // aktueller Zustand
	                
	                #sStateText := 'Leave Separator';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                // Freigabe an Separator, wenn alle Bedingungen erfüllt
	                #xReleaseTrolleySep1 := #xInSftyOk AND
	                #xCarouselRun AND
	                NOT #xWrongDirDetect AND
	                NOT #stActRequest.xLongSignal AND
	                NOT #stInOutJamEntr1.Status.bByte.%X3 AND
	                
	                (#stInPriorityManager.xReleaseOk AND #stActRequest.nDirection = 1 AND #stInSettings.xPrioDir1)
	                OR (#stActRequest.nDirection = 1 AND NOT #stInSettings.xPrioDir1)
	                OR (#stInPriorityManager.xReleaseOk AND #stActRequest.nDirection = 2 AND #stInSettings.xPrioDir2)
	                OR (#stActRequest.nDirection = 2 AND NOT #stInSettings.xPrioDir2)
	                OR (NOT #stInSettings.xPrioDir1 AND NOT #stInSettings.xPrioDir2) AND
	                
	                (#stActRequest.nDirection = 1 OR #stActRequest.nDirection = 2 OR #stActRequest.nDirection = 3);
	                
	                /////////  Timer  //////////
	                
	                // Reason for Stop
	                
	                // IF #stInMfr.xPause THEN
	                //     #tmpReasonForStop := "Rsn_MfrPausedTro";
	                // END_IF;
	                
	                IF NOT #xCarouselRun THEN
	                    #tmpReasonForStop := "Rsn_CarouselError";
	                END_IF;
	                
	                IF #xWrongDirDetect THEN
	                    #tmpReasonForStop := "Rsn_WrongDirection";
	                END_IF;
	                
	                IF #stActRequest.xLongSignal THEN
	                    #tmpReasonForStop := "Rsn_LongSignalDetected";
	                END_IF;
	                
	                //////// Transition ////////
	                
	                CASE #stActRequest.nDirection OF
	                    1, 2, 3:
	                        IF #fbSeparator1.stOutMainTro.nStatus = "SepState_Finish" THEN  // Separator meldet erfolgreiches Vereinzeln
	                            REGION NEU: Buchen 1->2
	                                IF #stActRequest.nDirection = 1 THEN
	                                    "FC_Route"(xInSingle := FALSE,
	                                               xInSeparate := FALSE,
	                                               xInSepEdge := TRUE,
	                                               xInSensor := FALSE,
	                                               xInDelay := FALSE,
	                                               stInCarrier := #stEmptyCarrier,
	                                               stOutBookingInfo => #stBookingInfo,
	                                               arJamEntr := #arInOutJamEntr1,
	                                               arOnTheWay := #arEmptyCarrier,
	                                               arJamExit := #arInOutJamExit2);
	                                END_IF;
	                            END_REGION
	                            
	                            REGION NEU: Buchen 1->3
	                                IF #stActRequest.nDirection = 2 THEN
	                                    "FC_Route"(xInSingle := FALSE,
	                                               xInSeparate := FALSE,
	                                               xInSepEdge := TRUE,
	                                               xInSensor := FALSE,
	                                               xInDelay := FALSE,
	                                               stInCarrier := #stEmptyCarrier,
	                                               stOutBookingInfo => #stBookingInfo,
	                                               arJamEntr := #arInOutJamEntr1,
	                                               arOnTheWay := #arEmptyCarrier,
	                                               arJamExit := #arInOutJamExit3);
	                                END_IF;
	                            END_REGION
	                            
	                            REGION NEU: Buchen 1->4
	                                IF #stActRequest.nDirection = 3 THEN
	                                    "FC_Route"(xInSingle := FALSE,
	                                               xInSeparate := FALSE,
	                                               xInSepEdge := TRUE,
	                                               xInSensor := FALSE,
	                                               xInDelay := FALSE,
	                                               stInCarrier := #stEmptyCarrier,
	                                               stOutBookingInfo => #stBookingInfo,
	                                               arJamEntr := #arInOutJamEntr1,
	                                               arOnTheWay := #arEmptyCarrier,
	                                               arJamExit := #arInOutJamExit4);
	                                END_IF;
	                            END_REGION
	                            
	                            
	                            
	                            #nState := #c1000_StepWaitForTrolley1;               // Nächster Schritt
	                        ELSIF #xReleaseTrolleySep1 AND #tOnSvReleaseSep.Q THEN      // Überwachungszeit nach Freigabe an Separator abgelaufen
	                            #tmpReasonForStop := "Rsn_SubError";                    // Timeout-Fehler im Separator: Freigabe erteilt, aber Separator gibt keine Rückmeldung an MainTRO
	                        END_IF;
	                    ELSE
	                        #tmpReasonForStop := "Rsn_GeneralError";                    // Allgemeiner Fehler
	                END_CASE;
	            END_REGION
	    END_CASE;
	    
	END_REGION
	
	REGION Beladekontrolle
	    //Beladekontrolle aktiviert
	    IF #stInSeparator1.Settings.xPluggedPart THEN
	        
	        //Trolley beladen
	        
	        // #xTrolleyWithPart :=
	        // #stInSeparator1.Settings.xPluggedPart AND
	        // #stInSeparator1.stSenInSep.xDbncdSensor
	        // AND #stInSeparator1.stSenPart.xDirectSensor
	        // AND #stInSeparator1.stSenPart.tDirectChangeLast > #stInSettings.tPartSenHigh;
	        
	        IF #stInSeparator1.Settings.xPluggedPart AND
	            #stInSeparator1.stSenInSep.xDbncdSensor
	            AND #stInSeparator1.stSenPart.xDirectSensor
	        //    AND #stInSeparator1.stSenPart.tDirectChangeLast > #stInSettings.tPartSenHigh
	        THEN
	            #xTrolleyWithPart := TRUE;
	        END_IF;
	        
	        //Trolley nicht beladen
	        #xTrolleyWoPart :=
	        NOT #xTrolleyWithPart AND //NEU
	        #stInSeparator1.Settings.xPluggedPart AND
	        #stInSeparator1.stSenInSep.xDbncdSensor AND
	        #stInSeparator1.stSenInSep.tDirectChangeLast > #stInSettings.tInSenHigh AND
	        NOT #stInSeparator1.stSenPart.xDirectSensor;
	        
	        //Plausibel?
	        IF #stInSeparator1.Settings.xPluggedPart AND ((#arInOutJamEntr1[1].bStatus.%X1 AND #xTrolleyWoPart) OR
	            (NOT #arInOutJamEntr1[1].bStatus.%X1 AND #xTrolleyWithPart))
	        THEN
	            #wErrorWord.%X2 := TRUE;
	        ELSE
	            #wErrorWord.%X2 := FALSE;
	        END_IF;
	    ELSE
	        #xTrolleyWithPart := #xTrolleyWoPart := FALSE; // Beladekontrolle (Sensor) nicht verbaut
	    END_IF;
	END_REGION
	
	REGION Timer - Überwachungszeit
	    
	    #tOnSwitchDelay(IN := #xStartSwitchDelay,
	                    PT := T#5s);
	    
	    #tOnSvReleaseSep(IN := ((#nState = #c1500_StepReleaseSep1) OR (#nState = #c2500_StepReleaseSep2)) AND #xReleaseTrolleySep1,
	                     PT := T#3s);
	    
	END_REGION
	
	REGION Haltegrund Schrittkette
	    
	    #stActRequest.nReasonForStop := #tmpReasonForStop;  //tmp aus Schrittkette übernehmen
	    
	END_REGION
	
	REGION Motorüberwachung
	    
	    IF (#nState >= #c1500_StepReleaseSep1) AND (#nState <= #c2500_StepReleaseSep2) AND #fbStateManager.tOutTimeInStep > T#4s AND NOT #xCarouselRun THEN
	        #xErrorMotor := TRUE;
	    ELSE
	        #xErrorMotor := FALSE;
	    END_IF;
	    
	END_REGION
	
	REGION StateManager
	    
	    #fbStateManager.stInSettings.nStartState := #c0100_StepUndefined;
	    #fbStateManager.stInSettings.nStartStateTestMode := #c0000_StepOff;
	    #fbStateManager.stInSettings.xActUnivTOn := TRUE;
	    #fbStateManager.stInSettings.xRestartAtStartState := FALSE;
	    #fbStateManager(xInFunctionEnabled := #stInOutMachineState.xRunning,
	                    xInReset := (* #stInMfr.xCancel OR *)#xInCancel,
	                    xInRunUnivTOn := #tmpActUnivTon,
	                    xInTestMode := #xInTestMode,
	                    nInOutState := #nState);
	END_REGION
	
	REGION Aufruf der Sub-TROs
	    
	    REGION Separator 1        
	        #fbSeparator1(xInReset := #xInCancel,
	                      stInOutMainState := #stInOutMachineState,
	                      stInSettings := #stInSeparator1.Settings,
	                      xInConveyorRun := #xCarouselRun,
	                      stInTrolley := #stInSeparator1.stSenInSep,
	                      xInReceiveReady := #xReleaseTrolleySep1,
	                      stInOutHMI := #stInOutHMI.UDT_Separator1,
	                      stInOutCarrDataInJam := #arInOutJamEntr1);
	        
	        #stInOutHMI.UDT_Separator1.stStatus.xNullBt := #stInOutHMI.UDT_Ctrl.xPause;
	        #stInOutHMI.UDT_Separator2.stStatus.xNullBt := #stInOutHMI.UDT_Ctrl.xPause;
	        
	        IF #nState >= 1000 AND #nState < 2000 THEN
	            #stInOutHMI.UDT_Separator1.stStatus.xMiniTrolley := FALSE; //1.Bein
	        ELSIF #nState >= 2000 THEN
	            #stInOutHMI.UDT_Separator1.stStatus.xMiniTrolley := TRUE; //2.Bein
	        END_IF;
	        
	    END_REGION
	    
	    REGION Switch 1
	        #fbSwitch1(stInSettings := #stInSwitch1.Settings,
	                   stInSenExit3 := #stInSwitch1.stSenExit3,
	                   stInSenExit4 := #stInSwitch1.stSenExit4,
	                   nInMainTroNo := #nInMainTroNo,
	                   nInSetSwitch := #nSetSwitch1,
	                   nInSwitchNo := 0,
	                   xInErrorQuit := "cTodoFalse",
	                   xInSftyOk := #xInSftyOk,
	                   xInAllRdyToStart := #xInAllRdyToStart,
	                   xInCarouselRun := #xCarouselRun,
	                   xInCancel := (* #stInMfr.xCancel OR *)#xInCancel,
	                   xInReqFinished := #tmpReqFinished,
	                   xOutSwEntrTo1 => #xOutSw1EntrTo1,
	                   xOutSwEntrTo2 => #xOutSw1EntrTo2,
	                   xOutSwExTo3 => #xOutSw1ExTo3,
	                   xOutSwExTo4 => #xOutSw1ExTo4,
	                   stInOutMachineState := #stInOutMachineState,
	                   stInOutHMI := #stInOutHMI.UDT_Switch1);
	        
	    END_REGION ;
	    
	    REGION Switch 2
	        #fbSwitch2(stInSettings := #stInSwitch2.Settings,
	                   stInSenExit3 := #stInSwitch2.stSenExit3,
	                   stInSenExit4 := #stInSwitch2.stSenExit4,
	                   nInMainTroNo := #nInMainTroNo,
	                   nInSetSwitch := #nSetSwitch2,
	                   nInSwitchNo := 0,
	                   xInErrorQuit := "cTodoFalse",
	                   xInSftyOk := #xInSftyOk,
	                   xInAllRdyToStart := #xInAllRdyToStart,
	                   xInCarouselRun := #xCarouselRun,
	                   xInCancel := (* #stInMfr.xCancel OR *)#xInCancel,
	                   xInReqFinished := #tmpReqFinished,
	                   xOutSwEntrTo1 => #xOutSw2EntrTo1,
	                   xOutSwEntrTo2 => #xOutSw2EntrTo2,
	                   xOutSwExTo3 => #xOutSw2ExTo3,
	                   xOutSwExTo4 => #xOutSw2ExTo4,
	                   stInOutMachineState := #stInOutMachineState,
	                   stInOutHMI := #stInOutHMI.UDT_Switch2);
	        
	    END_REGION ;
	    
	    REGION Barcode Reader 1
	        #fbBarcodeReader1(stInSettings := #stInBarcodeReader.Settings,
	                          xInHWError := false, // todo
	                          xInStartReading := #xStartScan,
	                          xInHandshakeDone := #xHandshakeScan,
	                          stInOutInterface := #stInOutCognexInterface,
	                          stInOutControlUnitCabinet := #stInOutMachineState,
	                          stInOutHMI := #stInOutHMI.UDT_BarcodeReader1);
	    END_REGION
	END_REGION
	
	REGION BT und NBT zählen
	    
	    #stActRequest.nActNoInSwitch := #fbSwitch1.stOutMainTro.nFifoElementCount;
	    
	END_REGION
	
	REGION Long-Signale von Switch verarbeiten
	    
	    (*  //Switch gibt LongSignal aus und hier wird es für MainTRO gemerkt
	      IF (#stActRequest.nDirection = 1 AND #fbSwitch1.stOutMainTro.xSen3Long) OR
	          (#stActRequest.nDirection = 2 AND #fbSwitch1.stOutMainTro.xSen4Long) THEN
	          #stActRequest.xLongSignal := TRUE;
	      ELSIF #fbSwitch1.stOutMainTro.xFifoEmpty THEN
	          #stActRequest.xLongSignal := FALSE;
	      END_IF;
	      
	      #tOnLongSignal(IN := #stActRequest.xLongSignal, //Extra Timer für Long-Signal um nach dieser Zeit einen einzelnen BT der noch im Switch ist zu quittieren
	                     PT := #stInSwitch1.Settings.tSenWaitAfterLong); //"TimeLongSignalQuitMax"
	      
	      #fbSwitch1.xInDeqExternal :=   //Dequeue wird hier an Switch weitergegeben falls (nachdem Sep gesperrt wurde) nur noch ein einzelenr BT vermisst wird nach LongSignal
	      NOT #fbSwitch1.xOutBtLeftSwitchWrongDir AND
	      ((#nSetSwitch1 = 1 OR #nSetSwitch1 = 2 OR #nSetSwitch1 = 3 OR #nSetSwitch1 = 4) AND #stActRequest.xLongSignal AND #tOnLongSignal.Q AND #stActRequest.nActNoInSwitch = 1);
	      
	      
	      IF #stActRequest.xLongSignal AND #tOnLongSignal.Q AND #stActRequest.nActNoInSwitch > 1 THEN //Falls nach LongSignal mehrere BT vermisst werden, werden die entsprechenden FIFO-Indizes an MFR als Missing ausgegeben
	          #nIndex := 1;
	          WHILE #nIndex <= 6 DO
	              IF #fbSwitch1.arOutFifo[#nIndex] > 0 AND #fbSwitch1.arOutFifo[#nIndex] <= 6 THEN
	                  #arReq[#nIndex].xMissingBT := TRUE;
	              ELSIF #fbSwitch1.arOutFifo[#nIndex] = 0 THEN
	                  #arReq[#nIndex].xMissingBT := FALSE;
	              END_IF;
	              #nIndex += 1;
	          END_WHILE;
	      ELSE //Wenn FIFO leer z.B. wegen Cancel, dann verschwinden hier die Missing Bits
	          #arReq[1].xMissingBT := FALSE;
	          #arReq[2].xMissingBT := FALSE;
	          #arReq[3].xMissingBT := FALSE;
	          #arReq[4].xMissingBT := FALSE;
	          #arReq[5].xMissingBT := FALSE;
	          #arReq[6].xMissingBT := FALSE;
	      END_IF; *)
	    
	END_REGION
	
	REGION WrongDirection Handling
	    
	    (*  IF #fbSwitch1.xOutBtLeftSwitchWrongDir THEN
	          #xWrongDirDetect := TRUE;
	      ELSIF #fbSwitch1.stOutMainTro.xFifoEmpty OR #stActRequest.xCancelled THEN
	          #xWrongDirDetect := FALSE;
	      END_IF;
	      
	      IF #xWrongDirDetect THEN
	          #nIndex := 1;
	          WHILE #nIndex <= 6 DO
	              IF #fbSwitch1.arOutFifo[#nIndex] > 0 AND #fbSwitch1.arOutFifo[#nIndex] <= 6 THEN
	                  #arReq[#fbSwitch1.arOutFifo[#nIndex]].xWrongDirection := TRUE;
	              ELSE
	                  #nIndex += 1;
	              END_IF;
	              #nIndex += 1;
	          END_WHILE;
	      ELSE
	          #arReq[1].xWrongDirection := FALSE;
	          #arReq[2].xWrongDirection := FALSE;
	          #arReq[3].xWrongDirection := FALSE;
	          #arReq[4].xWrongDirection := FALSE;
	          #arReq[5].xWrongDirection := FALSE;
	          #arReq[6].xWrongDirection := FALSE;
	      END_IF; *)
	    
	END_REGION
	
	REGION HMI
	    #stInOutHMI.UDT_Config.nTroType := 3; //1Sep2Switch
	    
	    #stInOutHMI.UDT_State.nState := #nState;
	    #stInOutHMI.UDT_State.sState := #sStateText;
	    
	    #stInOutHMI.UDT_State.xError := // Error TRO ->
	    #fbSeparator1.wOutErrorWord > 0 OR // Error Separator
	    #stInOutHMI.UDT_Separator1.stStatus.bStatus = 2 OR //Error Separator
	    #wErrorWord.%X5 OR //Belegung ohne Daten
	    #wErrorWord.%X10 OR  // Carrier kommt nicht an 
	    #stInOutHMI.UDT_BarcodeReader1.bStatus = 2;  //Error Reader
	    //Switch...
	END_REGION
	
	REGION Ausgänge schreiben
	    
	    REGION Output
	        #xOutCorrection := #xTmpCorrection;
	        
	        //Separator
	        #xOutStopper1 := #fbSeparator1.xOutStopper;
	        //Switch1
	        #xOutSw1EntrTo1 := #fbSwitch1.xOutSwEntrTo1;
	        #xOutSw1EntrTo2 := #fbSwitch1.xOutSwEntrTo2;
	        #xOutSw1ExTo3 := #fbSwitch1.xOutSwExTo3;
	        #xOutSw1ExTo4 := #fbSwitch1.#xOutSwExTo4;
	        //Switch2
	        #xOutSw2EntrTo1 := #fbSwitch2.xOutSwEntrTo1;
	        #xOutSw2EntrTo2 := #fbSwitch2.xOutSwEntrTo2;
	        #xOutSw2ExTo3 := #fbSwitch2.xOutSwExTo3;
	        #xOutSw2ExTo4 := #fbSwitch2.#xOutSwExTo4;
	        
	        #xOutJamAtEntry1 := #fbSeparator1.stOutMainTro.xJamAtEntry;
	        #xOutCarouselReq1 := (#nState >= #c1500_StepReleaseSep1) AND (#nState <= #c2500_StepReleaseSep2)  (* AND NOT #stInMfr.xPause *)AND NOT #xInAreaStopActive;
	        #xOutCarouselReq2 := #xOutCarouselReq1 AND
	        ((#stInSettings.xCar2NecLeft AND (#stActRequest.nDirection = 1 OR #stActRequest.nDirection = 3)) OR
	        (#stInSettings.xCar2NecRight AND (#stActRequest.nDirection = 2 OR #stActRequest.nDirection = 4)));
	    END_REGION
	    
	END_REGION
	
	REGION Letzten Schritt wegschreiben
	    #nOutStateLast := #fbStateManager.nStateLast;
	END_REGION
	
	REGION Priority Manager
	    #stOutPriorityManager.nTroId := #nInMainTroNo;
	    #stOutPriorityManager.stJamExit := #stInOutJamExit3;
	    // #stOutPriorityManager.stJamEntry := ; todo
	    #stOutPriorityManager.stSensor := #stInSeparator1.stSenInSep;
	    #stOutPriorityManager.xMovement := #xInCarouselRun1; // ??
	    //   #stOutPriorityManager.xTrolleyPresent := ; // todo für mengengesteuert
	    IF #stInSettings.xPrioDir1 THEN
	        #stOutPriorityManager.xTransportReq := (#stActRequest.nDirection = 1) AND #nState = #c2500_StepReleaseSep2;
	    ELSIF #stInSettings.xPrioDir2 THEN
	        #stOutPriorityManager.xTransportReq := (#stActRequest.nDirection = 2) AND #nState = #c2500_StepReleaseSep2;
	    END_IF;
	    
	END_REGION
END_FUNCTION_BLOCK

