FUNCTION_BLOCK "FB_ILS_MTRO_1Sep_SSCC"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      stInSettings : Struct   // Einstellungen MTRO
         nTroStopper1 : Int;
         xIsBorner : Bool;   // Bei diesem TRO werden immer neue Carrier angelegt (z.B. erster TRO bei Einfahrt in Gesamtanlage)
         sJamSrc : String[16];
         sJamDest : String[16];
         bSepType : Byte;   // X0: TRUE - erstes Bein halten, X1: TRUE- Beladekontrolle, X2: TRUE - Beladeerfassung
         tPartSenHigh : Time := T#500ms;
         tInSenHigh : Time := T#500ms;
         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   // Separator Parameter
         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;
      stInBarcodeReader : Struct
         Settings : "UDT_SettingsBarcodeReader";
      END_STRUCT;
      stInBarcodeReaderSSCC : Struct
         stSenEndMeasurement : "UDT_Sensor";
         xInPluggedSSCC : Bool := true;
         Settings : "UDT_SettingsBarcodeReader";
      END_STRUCT;
      stInSenJam2 : "UDT_Sensor";   // Stausensor Exit 2
      stInPriorityManager : "UDT_Response";   // Schnittstelle Prioritätenmanager (z.B. bei Kreuzungen)
      nInMainTroNo : UInt;   // TRO-Nummer
      xInSftyOk : Bool;   // Safety ok
      xInAllRdyToStart : Bool;   // Alles bereit
      xInCarouselRun : Bool;   // Förderer aktiv
      xInTestMode : Bool;   // Testmodus aktivieren
      xInCancel : Bool;   // TRO komplett rücksetzen
      xInAreaStopActive : Bool;   // Bereichshalt
      xInRelease : Bool;   // Freigabe/Anforderung
   END_VAR

   VAR_OUTPUT 
      xOutStopper1 : Bool;   // Separator Stopper öffnen (TRUE = Auf, FALSE = Zu)
      xOutJamAtEntry1 : Bool;   // Stau
      xOutCarouselReq : Bool;   // Förderer anfordern
      xOutCorrection : Bool;
      stOutPriorityManager : "UDT_Request";   // Schnittstelle Prioritätenmanager (z.B. bei Kreuzungen)
      nOutStateLast : Int;
      nOutScanResult : DInt;
      xOutNewScan : Bool;
      nOutScanResultSSCC : LInt;
      xOutNewScanSSCC : Bool;
      xOutBookedDir1 : Bool;
      stOutLastCarrier : "stCarrier";
   END_VAR

   VAR_IN_OUT 
      stInOutMachineState : "UDT_MainState";   // Maschinenzustand (Allgemien)
      stInOutCognexInterface : "stCognexInterface";   // Cognex Barcode Reader Interface
      stInOutCognexInterfaceSSCC : "stCognexInterface";
      arInOutJamEntr1 : Array[*] of "stCarrier";   // Daten Staubereich Eingang 1
      stInOutJamEntr1 : "stJamData";   // Konfig+Status-Daten Staubereich Eingang 1
      arInOutJamExit2 : Array[*] of "stCarrier";   // Carrier-Daten Staubereich Ausgang 1
      stInOutJamExit2 : "stJamData";   // Konfig+Status-Daten Staubereich Ausgang 1
      stInOutHMI : "UDT_HMI_TRO";   // HMI Schnittstelle 
   END_VAR

   VAR 
      fbBarcodeReader1 : "FB_BarcodeReaderCognex";   // Instanz Barcode-Reader
      fbBarcodeReaderSSCC : "FB_SsccBarcodeReader";   // Teile lesen (z.B. SSCC)
      fbSeparator1 : "FB_ILS_STRO_Sep";   // Intanz Separator 
      fbStateManager : "FB_StateManager";   // Schrittketten Manager
   END_VAR
   VAR RETAIN
      stActRequest : Struct   // Aktuelle Schrittkettendaten
         xNBT : Bool;   // Es handelt sich um einen Nullbügelträger
         xCancelled : Bool;   // Abbruchanforderung durch MFR
         nActArrIndex : USInt;
         nActReqIndex : USInt;   // 0 = nichts zu tun | 1-200: Aktuell bearbeiteter Index
         nDirection : USInt;   // 0: Es bleibt hier alles so wie es ist! | 1: E1->A2
         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 | 55: MFR hat TRO pausiert
         nActNoStopper : USInt;   // Anzahl der Stopper-öffnungen in diesem Auftrag
         nCntAtStartSep1 : UDInt;   // Absoluter Zähler an Separator 1 bei Start des Auftrages
         nCntAtStartExit2 : UDInt;   // Absoluter Zähler an Exit 2 bei Start des Auftrages
      END_STRUCT;
   END_VAR
   VAR 
      sStateText : String[100];   // Aktueller State der Schrittkette (textuell)
   END_VAR
   VAR RETAIN
      nState : Int;   // Schrittkette Schrittnummer
      xReleaseTrolleySep1 : Bool;   // Öffne-Befehl
      xErrorMotor : Bool;   // Fehler weil Motor-Run fehlt
      xMTRORdy : Bool;
   END_VAR
   VAR 
      tOnSvReleaseSep {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Supervision - Freigabe Separator
      tOnMonCarrArrive {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Überwachungszeit zwischen Carrier Online & Separator Sensor Belegung
      xTransportAllowed : Bool;
      fbSetAlarmAndLog : "FB_SetAlarmAndLog";
      xStartScan : Bool;
      nBarcodeResult : DInt;
      nBarcodeResultSSCC : LInt;
      wErrorWord : Word;
      fbMonitoringCarrNotArrive : "FB_Monitoring";
      fbMonitoringCarrWoData : "FB_Monitoring";
      nArrayMax : Int;
      stCarrierDummy { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : "stCarrier" := (1, (), 16#01);
      xTrolleyWithPart : Bool;
      xTrolleyWoPart : Bool;
      xHandshakeScan : Bool;
      xReadSecondLeg : Bool;
      fbEventLog_Route : "FB_EventLog_Booking";
      xCodeEven : Bool;
      nBarcodeHelp : DInt;
      sBarcodeString : String;
      fbEventLogCorrection1 : "FB_EventLog_Correction";
      fbEventLogCorrection2 : "FB_EventLog_Correction";
      fbEventLogCarrierUnknown : "FB_EventLog_Scan";
      fbEventLogCarrierNew : "FB_EventLog_Scan";
      fbEventLogGoodRead : "FB_EventLog_Scan";
      fbCarrierWait : "FB_CarrAccumulate1Sep";
      nBarcodeEmpty : Int;
      xStartScanSSCC : Bool;
      xHandshakeScanSSCC : Bool;
   END_VAR

   VAR_TEMP 
      xTemp : Bool;
      nTempStopReasonInternal : Word;
      nTempStopReasonExternal : Word;
      nIndex : USInt;
      tmpActUnivTon : Bool;
      xBtEmpty : 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";
      arEventCorrectionInfo : Array[0..1] of "stEventCorrectionInfo";
      arEmptyCarrier : Array[1..1] of "stCarrier";   // Platzhalter für CarrierArray
      tmpCarrierEmpty : "stCarrier";
      xEventLogRoute : Bool;
      stEventNew : "stEventScanInfo";
      stEventUnknown : "stEventScanInfo";
      xNewCarrier : Bool;
      xGoodRead : Bool;
      xTmpCorrection : Bool;
      xUnknownCarrier : Bool;
      stEventGoodRead : "stEventScanInfo";
      xBookedDir1 : Bool;
      stTempCarrier : "stCarrier";
   END_VAR

   VAR CONSTANT 
      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)
      c2300_StepCheckSep2 : Int := 2300;   // Separator checken
      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_1Sep
	Comment/Function: MainTRO-Function block for controlling TRO with Separator + Scanner(optional)  
	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-13 | -                  | Yüksel, Molina   | Header added on 13.12.2022
	002.000.000 | 2025-02-11 | -                  | Yüksel           | Adapt to ILS with Minitrolley, without external MFR
	                                                                   
	*)
	
	(*                 ----->
	                   Sep. 1        
	Entrance 1 -----------|---------- Exit 2
	
	*)
	
	(*Hierarchie
	Logistik = Priorität Modul (Vorfahrt, Ampel, mengengesteuert, zeitgesteuert, deterministisch)
	MainTRO = TRansportObject Modul (Koordinierung der Fördertechnik)
	SubTRO = Fördertechnik Modul (Ausführen der Fördertechnik)*)
	
	REGION Schrittkette Transport 
	    
	    CASE #nState OF
	        #c0000_StepOff:
	            REGION 0000: Off
	                // akt. Zustand
	                #sStateText := 'OFF';
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                #xCodeEven := FALSE;
	                #xOutNewScan := FALSE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                // SCHRITT 0 WIRD GEBRAUCHT!
	                //STATE MANAGER KÜMMERT SICH UM TRANSITION!
	            END_REGION
	            
	        #c0100_StepUndefined:
	            REGION 0100: Undefined
	                // akt. Zustand
	                #sStateText := 'Undefined';
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                #xMTRORdy := FALSE;
	                #xCodeEven := 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 // Wenn Pause an HMI gedrückt, dann warten
	                    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,
	                    //                           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_INBOUND"(xInStart := TRUE, //Führe Korrektur aus
	                    //                                       nInBarcodeScan := #nBarcodeResult,
	                    //                                       nInStation:= #stInBarcodeReader.Settings.nStation,
	                    //                                       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;
	                
	                #xHandshakeScan := TRUE;
	                
	                #nOutScanResult := #nBarcodeResult;
	                #xOutNewScan := TRUE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                IF #stInSeparator1.Settings.xPluggedPart AND #stInSettings.bSepType.%X2 THEN
	                    #nState := #c1300_StepCheckLoad;
	                    // #nState := #c1400_StepCheckSep1;
	                ELSIF #xCodeEven THEN
	                    #xCodeEven := FALSE;
	                    #nState := #c2000_StepWaitForTrolley2; //Scanner bestätigt zweites Bein gelesen -> Korrigieren!
	                ELSE
	                    #nState := #c1400_StepCheckSep1;
	                END_IF;
	                
	            END_REGION 
	            (*           
	                   "1SeStep_CheckCorrectBt":
	                       REGION 2000: Check if correct BT inside
	                           
	                           // akt. Zustand
	                           #sStateText := 'Check Correct Bt';
	                           
	                           ///////// Ausgänge /////////
	                           ///////// Statics //////////
	                           #xReleaseTrolleySep1 := FALSE;
	                           #xMTRORdy := FALSE;
	                           /////////  Timer  //////////
	                           
	                           //////// Transition ////////
	                           
	                           IF #stActRequest.nDirection = 1 THEN
	                               IF #fbSeparator1.stOutMainTro.xNullBtPresent AND #stActRequest.nActReqIndex <> 201 THEN
	                                   #arReq[#stActRequest.nActArrIndex].xNullBT := TRUE;
	                                   #arReq[#stActRequest.nActArrIndex].xError := TRUE;
	                                   #tmpReasonForStop := "Rsn_NullBt"; // Warten auf MFR-Befehl wegen unerwartetem NullBt (MFR kann z.B. Auftrag abbrechen)
	                               ELSIF
	                                   #fbSeparator1.stOutMainTro.xNullBtPresent AND #stActRequest.nActReqIndex = 201 THEN
	                                   #arReq[#stActRequest.nActArrIndex].xNullBT := TRUE;
	                                   #arReq[#stActRequest.nActArrIndex].xError := FALSE;
	                               ELSIF
	                                   NOT #fbSeparator1.stOutMainTro.xNullBtPresent AND #fbSeparator1.stOutMainTro.xBtPresent AND #stActRequest.nActReqIndex = 201 THEN
	                                   #arReq[#stActRequest.nActArrIndex].xNullBT := FALSE;
	                                   #arReq[#stActRequest.nActArrIndex].xError := TRUE;
	                               END_IF;
	                               //***************************
	                               IF (NOT #arReq[#stActRequest.nActArrIndex].xNullBT AND #fbSeparator1.stOutMainTro.xBtPresent AND #stActRequest.nActReqIndex <> 201) OR
	                                   (#arReq[#stActRequest.nActArrIndex].xNullBT AND #fbSeparator1.stOutMainTro.xBtPresent AND #stActRequest.nActReqIndex = 201)
	                               THEN   //BT korrekt und es geht weiter
	                                   #nState := "1SeStep_CheckJam";
	                               ELSIF (#fbStateManager.tOutTimeInStep > #stInSeparator1.Settings.tDelayToNextItem) AND (NOT #fbSeparator1.stOutMainTro.xBtPresent) THEN
	                                   #xBtEmpty := true;
	                               END_IF;
	                           ELSIF #stActRequest.nDirection <> 1 THEN
	                               #tmpReasonForStop := "Rsn_GeneralError";
	                           END_IF;
	                           
	                       END_REGION
	                       
	                   "1SeStep_CheckJam":
	                       REGION 2100: Check Jam
	                           // akt. Zustand
	                           #sStateText := 'Check Jam';
	                           ///////// Ausgänge /////////
	                           ///////// Statics //////////
	                           #xReleaseTrolleySep1 := FALSE;
	                           
	                           /////////  Timer  //////////
	                           //////// Transition ////////
	                           
	                           IF #stActRequest.nDirection = 1 THEN
	                               IF (#stInOutJamExit2.Status.nCount = #stInOutJamExit2.Config.nCapacitySet) OR #stInSenJam2.xJam (* #stInSeparator1.Settings.xPluggedJam AND #fbSeparator1.stOutMainTro.xJamAtEntry *) THEN
	                                   #tmpReasonForStop := "Rsn_JamNextError";
	                               ELSE
	                                   #nState := "1SeStep_CheckMotor";
	                               END_IF;
	                           ELSE
	                               #tmpReasonForStop := "Rsn_GeneralError";
	                           END_IF;
	                           
	                       END_REGION
	                       
	                   "1SeStep_CheckMotor":
	                       REGION 2150: Check Motor 
	                           //akt. Zustand
	                           #sStateText := 'Check Motor';
	                           
	                           ///////// Statics //////////
	                           #xReleaseTrolleySep1 := FALSE;
	                           
	                           // Transition
	                           IF NOT #xInCarouselRun AND #fbStateManager.tOutTimeInStep > T#4s THEN
	                               #xErrorMotor := TRUE;
	                               #tmpReasonForStop := "Rsn_CarouselError";
	                           ELSIF #xInCarouselRun THEN
	                               #xErrorMotor := FALSE;
	                               #nState := "1SeStep_CheckSep";
	                           END_IF;
	                           
	                       END_REGION *)
	            
	        #c1300_StepCheckLoad:
	            REGION 1300: Check Load 
	                
	                // akt. Zustand
	                #sStateText := 'Check Load';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xHandshakeScan := FALSE;
	                #xReleaseTrolleySep1 := FALSE;
	                #xOutNewScan := FALSE;
	                
	                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;
	                #xHandshakeScan := 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 := (NOT #fbCarrierWait.xOutLockSep1 AND #xInCarouselRun 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';
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                
	                #xReleaseTrolleySep1 := FALSE;
	                #xHandshakeScan := FALSE;
	                #xOutNewScan := FALSE;
	                
	               //Transition
	                IF NOT #stInOutHMI.UDT_Ctrl.xPause THEN // Wenn Pause an HMI gedrückt, dann warten
	                    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
	                        ELSIF #stInBarcodeReaderSSCC.xInPluggedSSCC THEN
	                            #nState := 2260; // SSCC Scannen
	                        ELSE
	                            #nState := #c2300_StepCheckSep2; // Nächster Schritt -> Separator
	                        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 := #c2300_StepCheckSep2;
	                    
	                END_REGION
	                
	                
	            2260:
	                REGION 2260: Scanner Trigger SSCC
	                    
	                    // akt. Zustand
	                    #sStateText := 'Scanner Trigger SSCC';
	                    
	                    ///////// Ausgänge /////////
	                    
	                    ///////// Statics //////////
	                    #xReleaseTrolleySep1 := FALSE;
	                    
	                    IF #fbBarcodeReaderSSCC.xOutReadyForNewScan THEN //Scanner bereit
	                        #xStartScanSSCC := TRUE; //Startbefehl 
	                    END_IF;
	                    
	                    /////////  Timer  //////////
	                    
	                    //////// Transition ////////
	                    
	                    IF #fbBarcodeReaderSSCC.xOutReadingDone THEN //Scanner Rückmeldung
	                        #xStartScanSSCC := FALSE;
	                        #nState := 2270; //Weiter
	                    END_IF;
	                END_REGION
	                
	            2270:
	                REGION 2270: Scan SSCC Eval
	                    // akt. Zustand
	                    #sStateText := 'Scanner Evaluation 1';
	                    
	                    ///////// Ausgänge /////////
	                    
	                    ///////// Statics //////////
	                    #xReleaseTrolleySep1 := FALSE;
	                    
	                    #nBarcodeResultSSCC := #fbBarcodeReaderSSCC.#nOutBarcode;
	                    
	                    #xHandshakeScanSSCC := TRUE;
	                    
	                    #nOutScanResultSSCC := #nBarcodeResultSSCC;
	                    #xOutNewScanSSCC := TRUE;
	                    
	                    // IF #stInBarcodeReaderSSCC.stSenEndMeasurement.xDirectSensor THEN
	                        #nState := #c2300_StepCheckSep2;
	                    // END_IF;
	                    
	                END_REGION
	              
	        #c2300_StepCheckSep2:
	            REGION 2300: Check Separator                
	                // akt. Zustand
	                #sStateText := 'Check Sep';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xHandshakeScanSSCC := FALSE;
	                #xOutNewScanSSCC := false;
	                
	                #xHandshakeScan := FALSE;
	                #xReleaseTrolleySep1 := FALSE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                #tmpActUnivTon := TRUE;
	                IF #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForReq" THEN // Separator ist bereit
	                    #nState := #c2400_StepCheckJam;
	                ELSIF #fbStateManager.tOutUnivTonR > T#3s THEN
	                    #tmpReasonForStop := "Rsn_SubError";    // SubTRO: Separator ist nicht bereit
	                END_IF;
	                
	            END_REGION
	            
	        #c2400_StepCheckJam:
	            REGION 2400: Check Jam
	                // akt. Zustand
	                #sStateText := 'Check Jam';
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                /////////  Timer  //////////
	                //////// Transition ////////
	                
	                IF (#stInOutJamExit2.Status.nCount = #stInOutJamExit2.Config.nCapacitySet) //Zielstaubereich ist datentechnisch voll
	                    OR (#stInSeparator1.Settings.xPluggedJam AND #stInSenJam2.xJam) // Stausensor
	                THEN
	                    #tmpReasonForStop := "Rsn_JamNextError";
	                ELSE
	                    #nState := #"c2500_StepReleaseSep2";
	                END_IF;
	                
	            END_REGION
	            
	        #c2500_StepReleaseSep2:
	            REGION 2500: Release Sep Second          
	                // akt. Zustand
	                #sStateText := 'Leave Sep';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #stOutPriorityManager.xTransportReq := #xInSftyOk AND #xInCarouselRun (* AND NOT #stInMfr.xPause *) AND NOT #xInAreaStopActive;
	                
	                
	                #xReleaseTrolleySep1 := #xInSftyOk AND #xInCarouselRun (* AND NOT #stInMfr.xPause *) AND NOT #fbCarrierWait.xOutLockSep1 AND NOT #xInAreaStopActive AND #stInPriorityManager.xReleaseOk AND NOT #stInOutJamEntr1.Status.bByte.%X3; // Freigabe an Separator
	                
	                //////// Transition ////////    
	                IF #fbSeparator1.stOutMainTro.nStatus = "SepState_Finish" THEN
	                    #stTempCarrier := #arInOutJamEntr1[1];
	                    //Hier könnte man noch einbauen, ob optional nicht gebucht wird, z.B. für Fehlerkorrektur
	                    REGION Daten buchen 1 -> 2
	                            "FC_Route"(xInSingle := FALSE,
	                                       xInSeparate := FALSE,
	                                       xInSepEdge := TRUE,
	                                       xInSensor := FALSE,
	                                       xInDelay := FALSE,
	                                       stInCarrier := #stEmptyCarrier,
	                                       stOutBookingInfo => #stBookingInfo,
	                                       arJamEntr := #arInOutJamEntr1,
	                                       arOnTheWay := #arEmptyCarrier,
	                                       arJamExit := #arInOutJamExit2);
	                            #xBookedDir1 := TRUE;
	                        END_REGION
	                        #xEventLogRoute := TRUE;
	                        #nState := 2600;//#c1000_StepWaitForTrolley1;
	                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
	            
	        2600:
	            #xReleaseTrolleySep1 := FALSE;
	            
	            IF #stInBarcodeReaderSSCC.stSenEndMeasurement.xDirectSensor THEN
	                #nState := #c1000_StepWaitForTrolley1;
	            END_IF;
	            
	            
	(*        "1SeStep_WaitForConfirm":
	            REGION 2600: Wait for Confirm after Leaving - NIU 
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                // #xRunCarousels := TRUE;
	                /////////  Timer  ////////// 
	                
	                //////// Transition ////////
	                
	                IF #fbSeparator1.stOutMainTro.nStatus = "SepState_Finish" OR #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForFirstLeg" THEN
	                    #arReq[#stActRequest.nActArrIndex].xSepRelease := TRUE;
	                    #arReq[#stActRequest.nActArrIndex].xDone := TRUE;
	                    #nState := "1SeStep_WaitForMfr";
	                    // #nState := "1SeStep_CheckReqFinished";
	                END_IF;
	                
	            END_REGION *)
	            
	    END_CASE;
	    
	END_REGION
	
	REGION Carr Accumulate
	    #fbCarrierWait.stInTimes.tFreeSen1 := #stInSeparator1.Settings.tSenFree;
	    #fbCarrierWait.stInTimes.tTimerWait := #stInSeparator1.Settings.tSenWait;
	    #fbCarrierWait(stInSensor1 := #stInSeparator1.stSenInSep,
	                   xInConvActive := #xInCarouselRun);
	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;
	            
	            //Trolley nicht beladen
	            #xTrolleyWoPart :=
	            #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 Dummy Handling
	        
	        #nArrayMax := DINT_TO_INT(UPPER_BOUND(ARR := #arInOutJamEntr1, DIM := 1));
	        
	        REGION Dummy erstellen
	            IF #stInSettings.xIsBorner THEN
	                IF #nBarcodeEmpty > 1999 OR #nBarcodeEmpty < 1000 THEN
	                    #nBarcodeEmpty := 1000;
	                ELSE
	                    IF #nState = 2500 AND #fbSeparator1.xOutTrolleyReleasedEdge THEN
	                        #nBarcodeEmpty += 1;
	                    END_IF;
	                END_IF;
	                
	                IF #fbSeparator1.stOutMainTro.xBtPresent AND
	                    #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForReq" AND
	                    #stInSeparator1.stSenInSep.tDirectChangeLast > #stInSettings.tDummyTimeBorner AND
	                    #arInOutJamEntr1[1] = #tmpCarrierEmpty
	                THEN
	                    #arInOutJamEntr1[1].nBarcode := #nBarcodeEmpty;
	                    #arInOutJamEntr1[1].bStatus.%X0 := TRUE;
	                    #arInOutJamEntr1[1].nDestination := #stInSettings.nBornerDest;
	                END_IF;
	            ELSE
	                IF #fbSeparator1.stOutMainTro.xBtPresent AND
	                    #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForReq" AND
	                    #stInSeparator1.stSenInSep.tDirectChangeLast > T#3s AND // variabel machen, Dummy immer anlegen?, Unterscheidung bei Borner?
	                    #arInOutJamEntr1[1] = #tmpCarrierEmpty
	                THEN
	                    #arInOutJamEntr1[1].nBarcode := #cCarrierDummy;
	                    #arInOutJamEntr1[1].bStatus.%X0 := TRUE;
	                END_IF;
	            END_IF;
	            
	        END_REGION
	        
	        REGION Dummy löschen
	            IF #nArrayMax > 1 THEN //Staubereich mit Capacity > 0
	                IF (#arInOutJamEntr1[1].nBarcode = 1  (* #stCarrierDummy *)) //Dummy an erster Position
	                    AND #arInOutJamEntr1[2] = #tmpCarrierEmpty AND //Keine Daten an zweiter Position
	                    NOT #stInSeparator1.stSenInSep.xDirectSensor AND #stInSeparator1.stSenInSep.tDirectChangeLast > T#8s AND //keine Belegung
	                    #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForBt"  AND #fbSeparator1.#fbStateManager.tOutTimeInStep > T#8s //Separator Zustand abfragen
	                THEN
	                    #arInOutJamEntr1[1] := #tmpCarrierEmpty; //Dummy löschen
	                ELSIF (#arInOutJamEntr1[1].nBarcode = 1 (* #stCarrierDummy *)) //Dasselbe wie oben aber mit nachrücken
	                    AND (#arInOutJamEntr1[2].nBarcode = 1 (* #stCarrierDummy *) OR #arInOutJamEntr1[2].nBarcode > 1 (* #stCarrierDummy *)) AND
	                    NOT #stInSeparator1.stSenInSep.xDirectSensor AND #stInSeparator1.stSenInSep.tDirectChangeLast > T#8s AND
	                    #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForBt"  AND #fbSeparator1.#fbStateManager.tOutTimeInStep > T#8s //Separator Zustand abfragen
	                THEN
	                    #arInOutJamEntr1[1] := #tmpCarrierEmpty;
	                    "FC_TSHR_Shift"(xInShiftBack := TRUE,
	                                    arTSHR := #arInOutJamEntr1);
	                END_IF;
	            ELSIF #nArrayMax = 1 THEN
	                IF (#arInOutJamEntr1[1].nBarcode = 1 (* #stCarrierDummy *)) AND
	                    NOT #stInSeparator1.stSenInSep.xDirectSensor AND #stInSeparator1.stSenInSep.tDirectChangeLast > T#8s AND
	                    #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForBt"  AND #fbSeparator1.#fbStateManager.tOutTimeInStep > T#8s //Separator Zustand abfragen
	                THEN
	                    #arInOutJamEntr1[1] := #tmpCarrierEmpty;
	                END_IF;
	            END_IF;
	            
	        END_REGION
	        
	    END_REGION
	    
	    REGION Monitoring
	        
	        REGION Carrier kommt nicht an / Daten ohne Belegung
	            #tOnMonCarrArrive(IN := #arInOutJamEntr1[1].bStatus.%X0 AND NOT #stInSeparator1.stSenInSep.xDirectSensor,
	                              PT := #stInSeparator1.Settings.tDelayToNextItem,
	                              Q => #wErrorWord.%X10); //TODO
	            
	            #fbMonitoringCarrNotArrive.stSettings.nErrorType := 1;
	            #fbMonitoringCarrNotArrive.stSettings.xErrLedIfMachineIsOff := TRUE;
	            #fbMonitoringCarrNotArrive.stSettings.xSelfQuit := TRUE;
	            #fbMonitoringCarrNotArrive(wInTextListId_Function := 1000,
	                                       sInPrefix := #stInOutHMI.UDT_Config.sName,
	                                       wInTextListId_Message := 1000,
	                                       sInSuffix := '',
	                                       xInQuitError := #stInOutMachineState.xQuitError,
	                                       xInSignal := NOT #wErrorWord.%X10,
	                                       sInAlarmMessage := 'Carrier kommt nicht am Separator an',
	                                       InOutMachineState:=#stInOutMachineState);
	            
	        END_REGION
	        
	        REGION Carrier ohne Daten
	            
	            IF #stInSeparator1.stSenInSep.xDbncdSensor AND #stInSeparator1.stSenInSep.tDirectChangeLast > T#5s AND NOT #arInOutJamEntr1[1].bStatus.%X0 THEN
	                #wErrorWord.%X5 := TRUE;
	            ELSE
	                #wErrorWord.%X5 := FALSE;
	            END_IF;
	            
	            #fbMonitoringCarrWoData.stSettings.nErrorType := 1;
	            #fbMonitoringCarrWoData.stSettings.xErrLedIfMachineIsOff := TRUE;
	            #fbMonitoringCarrWoData.stSettings.xSelfQuit := TRUE;
	            #fbMonitoringCarrWoData(wInTextListId_Function := 1000,
	                                    sInPrefix := #stInOutHMI.UDT_Config.sName,
	                                    wInTextListId_Message := 1001,
	                                    sInSuffix := '',
	                                    InOutMachineState := #stInOutMachineState,
	                                    xInQuitError := #stInOutMachineState.xQuitError,
	                                    xInSignal := NOT #wErrorWord.%X5,
	                                    sInAlarmMessage := 'Sep-Belegung ohne Daten');
	        END_REGION
	        
	    END_REGION
	
	REGION Motorüberwachung
	    IF (#nState = #c1500_StepReleaseSep1 OR #nState = #c2500_StepReleaseSep2) AND #fbStateManager.tOutTimeInStep > T#4s AND NOT #xInCarouselRun THEN
	        #wErrorWord.%X1 := TRUE;
	    ELSE
	        #wErrorWord.%X1 := FALSE;
	    END_IF;
	END_REGION
	
	REGION Timer - Überwachungszeit
	    #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 StateManager
	    
	    #fbStateManager.stInSettings.nStartState := #c0100_StepUndefined;
	    #fbStateManager.stInSettings.nStartStateTestMode := #c0000_StepOff;
	    #fbStateManager.stInSettings.xActUnivTOn := TRUE;
	    #fbStateManager.stInSettings.xRestartAtStartState := FALSE;
	    #fbStateManager(xInFunctionEnabled := #stInOutMachineState.xRunning, // AND #xInSftyOk,
	                    xInReset := #stInOutHMI.UDT_Ctrl.xReset OR #xInCancel,
	                    xInRunUnivTOn := #tmpActUnivTon,
	                    xInTestMode := #xInTestMode,
	                    nInOutState := #nState);
	    
	END_REGION
	
	REGION Aufruf der Sub-TROs
	    
	    REGION Separator 1
	        
	        #fbSeparator1(xInReset:= #stInOutHMI.UDT_Ctrl.xReset OR #xInCancel,
	                      xInErrQuit := #stInOutHMI.UDT_Ctrl.xErrorQuit,
	                      stInOutMainState := #stInOutMachineState,
	                      stInSettings := #stInSeparator1.Settings,
	                      xInConveyorRun := #xInCarouselRun,
	                      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 Barcode Reader 1
	        #fbBarcodeReader1(stInSettings := #stInBarcodeReader.Settings,
	                          xInReset := #stInOutHMI.UDT_Ctrl.xReset OR #xInCancel,
	                          xInHWError := false, // todo
	                          xInStartReading := #xStartScan,
	                          xInHandshakeDone := #xHandshakeScan,
	                          stInOutInterface := #stInOutCognexInterface,
	                          stInOutControlUnitCabinet := #stInOutMachineState,
	                          stInOutHMI := #stInOutHMI.UDT_BarcodeReader1);
	    END_REGION
	    
	    REGION Barcode Reader SSCC
	        #fbBarcodeReaderSSCC(stInSettings := #stInBarcodeReaderSSCC.Settings,
	                          xInReset := #stInOutHMI.UDT_Ctrl.xReset OR #xInCancel,
	                          xInHWError := false, // todo
	                          xInStartReading := #xStartScanSSCC,
	                          xInHandshakeDone := #xHandshakeScanSSCC,
	                          stInOutInterface := #stInOutCognexInterfaceSSCC,
	                          stInOutControlUnitCabinet := #stInOutMachineState,
	                          stInOutHMI := #stInOutHMI.UDT_BarcodeReader2);
	    END_REGION
	    
	END_REGION
	
	REGION HMI
	    #stInOutHMI.UDT_Config.nTroType := 1; //1Sep
	    
	    #stInOutHMI.UDT_State.nState := #nState; //Schrittkette
	    #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
	    ;
	    
	END_REGION
	
	REGION Ausgänge schreiben
	    REGION Output       
	        #xOutStopper1 := #fbSeparator1.xOutStopper;
	        #xOutJamAtEntry1 := #fbSeparator1.stOutMainTro.xJamAtEntry;
	        #xOutCarouselReq := (#nState = #c1500_StepReleaseSep1) OR (#nState = #c2500_StepReleaseSep2) AND NOT #stInOutHMI.UDT_Ctrl.xPause AND NOT #xInAreaStopActive;
	        #xOutCorrection := #xTmpCorrection;
	        
	        #xOutBookedDir1 := #xBookedDir1;
	        #stOutLastCarrier := #stTempCarrier;
	    END_REGION
	    
	END_REGION
	
	REGION Event Logs
	    REGION Event: Route (Umbuchung)
	        #stBookingInfo.sJamSrc := #stInSettings.sJamSrc;
	        #stBookingInfo.sJamDest := #stInSettings.sJamDest;
	        #fbEventLog_Route(xInTrigger := #xEventLogRoute,
	                          stInEventBookingInfo := #stBookingInfo);
	    END_REGION
	END_REGION
	
	// Pro Event wird ein Aufruf benötigt
	REGION Korrekturvorgänge loggen
	    
	    #fbEventLogCorrection1(xInTrigger := #arEventCorrectionInfo[0].xTrigger,
	                           stInEventCorrectionInfo := #arEventCorrectionInfo[0]);
	    
	    #fbEventLogCorrection2(xInTrigger := #arEventCorrectionInfo[1].xTrigger,
	                           stInEventCorrectionInfo := #arEventCorrectionInfo[1]);
	    
	    #fbEventLogCarrierUnknown(xInTrigger := #xUnknownCarrier,
	                              stInEventScanInfo := #stEventUnknown);
	    
	    #fbEventLogCarrierNew(xInTrigger := #xNewCarrier,
	                          stInEventScanInfo := #stEventNew);
	    
	    #fbEventLogGoodRead(xInTrigger := #xGoodRead,
	                        stInEventScanInfo := #stEventGoodRead);
	END_REGION
	
	REGION Letzten Schritt wegschreiben
	    #nOutStateLast := #fbStateManager.nStateLast;
	END_REGION
	
	REGION Priority Manager
	    #stOutPriorityManager.nTroId := #nInMainTroNo;
	    #stOutPriorityManager.stJamExit := #stInOutJamExit2;
	    // #stOutPriorityManager.stJamEntry := ; todo
	    #stOutPriorityManager.stSensor := #stInSeparator1.stSenInSep;
	    #stOutPriorityManager.xMovement := #xInCarouselRun;
	    //   #stOutPriorityManager.xTrolleyPresent := ; // todo für mengengesteuert
	    #stOutPriorityManager.xTransportReq := (* (#stActRequest.nDirection = 1) AND *) #nState = #c2500_StepReleaseSep2;
	    
	END_REGION
END_FUNCTION_BLOCK

