FUNCTION_BLOCK "FB_ILS_MTRO_Vario_workStation"
{ 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 := 16#0;   // 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;
      stInVario : Struct   // Schnittstelle Vario
         stSenFinger : "UDT_Sensor";
         stSenNextSep : "UDT_Sensor";
         stSenJam : "UDT_Sensor";
         stSenLastPos : "UDT_Sensor";
         stSenSepVario : "UDT_Sensor";
         stSenCarrInPos : "UDT_Sensor";
         Settings : "SettingsVario";
         SettingsVarioFingerMonitoring : "UDT_SettingsVarioFingerMonitoring";
      END_STRUCT;
      stInSeparator1 : Struct   // Schnittstelle Separator (vor Vario Förderer)
         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   // Schnittstelle Barcode-Leser
         Settings : "UDT_SettingsBarcodeReader";
      END_STRUCT;
      stInPriorityManager : "UDT_Response";   // Schnittstelle Prioritätenmanager (z.B. bei Kreuzungen)
      nInMainTroNo : UInt;   // TRO Nummer
      xInRelease : Bool;
      xInSftyOk : Bool;   // Safety 
      xInMotorProtection : Bool;   // Motorschutz Vario
      xInAllRdyToStart : Bool;   // Alles bereit
      xInJamAtExit2 : Bool;   // Stau am Ausgang
      xInTestMode : Bool;   // Testmodus
      xInReset : Bool;   // Baustein Reset
      xInErrorQuit : Bool;
      xInAutomatic : Bool;
      stInStationSensor : "UDT_Sensor";
      xInReleaseStation : Bool;
   END_VAR

   VAR_OUTPUT 
      stOutPriorityManager : "UDT_Request";   // Schnittstelle Prioritätenmanager (z.B. bei Kreuzungen)
      xOutStopper : Bool;   // Separator Stopper 
      xOutVarioMotor : Bool;   // Vario Motor 
      xOutJamAtEntry1 : Bool;   // Stau Eingang
      xOutCarouselReq : Bool;   // Motor anfordern (Prüfen)
      xOutCorrection : Bool;
      nOutStateLast : Int;
      nOutScanResult : DInt;
      xOutNewScan : Bool;
      xOutBooked : Bool;
      stOutLastCarrier : "stCarrier";
   END_VAR

   VAR_IN_OUT 
      arInOutJamEntr : Array[*] of "stCarrier";   // Daten Staubereich Eingang 1
      stInOutJamEntr : "stJamData";
      arInOutJamFinger : Array[*] of "stCarrier";   // Daten Staubereich Finger
      stInOutJamFinger : "stJamData";
      stInOutMachineState : "UDT_MainState";   // Maschinenzustand (Allgemien)
      stInOutCognexInterface : "stCognexInterface";   // Cognex Barcode Reader Interface
      stInOutHMI : "UDT_HMI_TRO";   // HMI TRO
   END_VAR

   VAR 
      fbBarcodeReader : "FB_BarcodeReaderCognex";
      fbStateManager { S7_SetPoint := 'False'} : "FB_StateManager";
      fbSeparator1 { S7_SetPoint := 'False'} : "FB_ILS_STRO_Sep";
      fbConvVario : "FB_ILS_STRO_Vario_WorkStation";
      fbSepWait : "FB_CarrAccumulate1Sep";
   END_VAR
   VAR RETAIN
      stActRequest : Struct
         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["cStateText"];   // Aktueller State der Schrittkette (textuell)
   END_VAR
   VAR RETAIN
      nState : Int;
   END_VAR
   VAR 
      nLastReqIndex : USInt;
      tOfLastPos {InstructionName := 'TOF_TIME'; LibVersion := '1.0'} : TOF_TIME;   // Flanke des letzten Trolleys verarbeiten
      tOnSvReleaseSep {InstructionName := 'TON_TIME'; LibVersion := '1.0'; S7_SetPoint := 'False'} : TON_TIME;   // Supervision - Freigabe Separator
   END_VAR
   VAR RETAIN
      xReleaseTrolleySep1 : Bool;   // Öffne-Befehl
      xLastPosReached : Bool;
      xErrorMotor : Bool;   // Fehler weil Motor-Run fehlt
      xMTRORdy : Bool;
   END_VAR
   VAR 
      xStartScan { S7_SetPoint := 'True'} : Bool;
      xHandshakeScan : Bool;
      nBarcodeResult : DInt;
      xTrolleyWithPart : Bool;
      xTrolleyWoPart : Bool;
      xVarioReq : Bool;
      xReadSecondLeg : Bool;
      wErrorWord : Word;
      stCarrierDummy { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : "stCarrier" := (1, (), 16#01);
      nArrayMax : Int;
      fbMonitoringCarrNotArrive : "FB_Monitoring";
      fbMonitoringCarrWoData : "FB_Monitoring";
      tOnMonCarrArrive {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;   // Überwachungszeit zwischen Carrier Online & Separator Sensor Belegung
      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";
      fbAlarmVarioMTinPos : "FB_SetAlarmAndLog";
      nBarcodeEmpty : Int;
      nBarcodeReader_Result : DInt;
      FTrig_CarrInPos {InstructionName := 'F_TRIG'; LibVersion := '1.0'} : F_TRIG;
      xCarrierSensorPass : Bool;
   END_VAR

   VAR_TEMP 
      xTemp : Bool;
      nIndex : USInt;
      tmpActUnivTon : Bool;
      xBtEmpty : Bool;
      xVarioRelease : Bool;
      xVarioTrigger : 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
      nArrayMaxEntry : DInt;
      nArrayMaxFinger : DInt;
      nArrayMaxExit : DInt;
      stEmptyCarrier : "stCarrier";
      stBookingInfo : "stEventBookingInfo";
      arEmptyCarrier : Array[1..1] of "stCarrier";   // Platzhalter für CarrierArray
      tmpCarrierEmpty : "stCarrier";
      xEventLogRoute : Bool;
      arEventCorrectionInfo : Array[0..1] of "stEventCorrectionInfo";
      stEventNew : "stEventScanInfo";
      stEventUnknown : "stEventScanInfo";
      xNewCarrier : Bool;
      xGoodRead : Bool;
      xTmpCorrection : Bool;
      xUnknownCarrier : Bool;
      stEventGoodRead : "stEventScanInfo";
      xBooked : Bool;
      stTempCarrier : "stCarrier";
   END_VAR

   VAR CONSTANT 
      c0000_StepOff : Int := 0;   // Aus
      c0100_StepUndefined : Int := 100;   // Undefiniert
      c1000_StepWaitForTrolley1 : Int := 1000;   // Warte auf Trolley
      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 prüfen
      c2500_StepWaitForConvRdy : Int := 2500;   // Vario anforderung
      c2600_StepReleaseSep2 : Int := 2600;   // Freigabe an Separator
      c2700_WaitForCarrInPos : Int := 2700;   // Warte bis Carrier den Sensor "In Position" erreicht
      cCarrierDummy : Int := 1;
   END_VAR


BEGIN
	REGION Arraygrößen
	    #nArrayMaxEntry := UPPER_BOUND(ARR := #arInOutJamEntr, DIM := 1);
	    #nArrayMaxFinger := UPPER_BOUND(ARR := #arInOutJamFinger, DIM := 1);
	END_REGION
	
	REGION Schrittkette
	    
	    CASE #nState OF
	        #c0000_StepOff:
	            REGION 0000: Off
	                // akt. Zustand
	                #sStateText := 'OFF';
	                ///////// Ausgänge /////////
	                #xLastPosReached := FALSE;
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                #xVarioTrigger := FALSE;
	              //  #xVarioReq := FALSE;
	                #xCodeEven := FALSE;
	                #xOutNewScan := FALSE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                
	                //STATE MANAGER KÜMMERT SICH UM TRANSITION!
	            END_REGION
	            
	        #c0100_StepUndefined:
	            REGION 0100: Undefined
	                // akt. Zustand
	                #sStateText := 'Undefined';
	                ///////// Ausgänge /////////
	                #xLastPosReached := FALSE;
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                #xVarioTrigger := FALSE;
	              //  #xVarioReq := 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 /////////
	                #xLastPosReached := FALSE;
	                #xCarrierSensorPass := FALSE;
	                
	                ///////// Statics //////////
	                
	                #xReleaseTrolleySep1 := FALSE;
	                #xVarioReq := FALSE;
	                #xVarioTrigger := FALSE;
	                #xMTRORdy := TRUE;
	                #xCodeEven := FALSE;
	                #xOutNewScan := FALSE;
	                
	                //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 #arInOutJamEntr[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
	            
	            (* "VarioStep_Emptying":
	                REGION 1500: Emptying
	                    // akt. Zustand
	                    #sStateText := 'Emptying';
	                    
	                    ///////// Ausgänge /////////
	                    
	                    ///////// Statics //////////
	                    #xReleaseTrolleySep1 := FALSE;
	                    #xVarioTrigger := FALSE;
	                    #xMTRORdy := FALSE; // Handshake an JobManager!
	                    
	                    /////////  Timer  //////////
	                    
	                    //////// Transition ////////
	                    
	                    IF #stInVario.SenJam.xDirectEdgePos OR #stInVario.SenNextSep.xDirectEdgePos OR #fbStateManager.tOutTimeInStep > T#20s THEN
	                        #xVarioRelease := FALSE;
	                    ELSE
	                        #xVarioRelease := TRUE;
	                    END_IF;
	                    
	                    IF #stInVario.SenLastPos.xDirectEdgePos AND NOT #tOfLastPos.Q AND #arInOutJamFinger[1].bStatus.%X0 THEN
	                        "FC_Route"(xInSingle := FALSE,
	                                   xInSeparate := FALSE,
	                                   xInSepEdge := TRUE,
	                                   xInSensor := FALSE,
	                                   xInDelay := FALSE,
	                                   stInCarrier := #stEmptyCarrier,
	                                   stOutBookingInfo => #stBookingInfo,
	                                   arJamEntr := #arInOutJamFinger,
	                                   arOnTheWay := #arEmptyCarrier,
	                                   arJamExit := #arInOutJamExit);
	                    END_IF;
	                    
	                    IF #fbConvVario.xOutConveyorReady THEN         
	                        #nState := "VarioStep_WaitForMfr";
	                    ELSE
	                        #tmpReasonForStop := "Rsn_GeneralError";
	                    END_IF;
	                END_REGION *)
	            
	        #c1100_StepScannerTrig1:
	            REGION 1100: Scanner Trigger 1
	                
	                // akt. Zustand
	                #sStateText := 'Scanner Trigger 1';
	                
	                ///////// Ausgänge /////////
	                
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                
	                IF #fbBarcodeReader.xOutReadyForNewScan THEN //Scanner bereit
	                    #xStartScan := TRUE; //Startbefehl 
	                END_IF;
	                
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                
	                IF #fbBarcodeReader.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 #fbBarcodeReader.xOutCodeGood THEN
	                    #nBarcodeResult := LINT_TO_DINT(#fbBarcodeReader.#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 #fbBarcodeReader.#xOutCodeBad THEN
	                    #nBarcodeResult := #arInOutJamEntr[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)
	                    (#arInOutJamEntr[1].nBarcode = 0 OR #arInOutJamEntr[1].nBarcode = 1) THEN    //Wenn keine Daten oder ein Dummy erwartet werden   
	                    
	                    #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 := #arInOutJamEntr[1]);
	                    // END_REGION
	                    
	                    
	                    // IF NOT #xTemp THEN
	                    //     ;
	                    //     // #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    // ELSE
	                    //     #arInOutJamEntr[1].nDestination := "cDestNONE"; //neuer Carrier // TODO
	                    //     //     #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    //     #arInOutJamEntr[1].nBarcode := #nBarcodeResult; //Hier werden neue Carrier im System angelegt
	                    //     #arInOutJamEntr[1].bStatus.%X0 := TRUE;
	                    // END_IF;
	                    
	                    // REGION Event Log New Carrier
	                    
	                    //     #xNewCarrier := TRUE; // Trigger
	                    //     #stEventNew.nScanType := 2;
	                    //     #stEventNew.sBarcode := DELETE(IN := DINT_TO_STRING(#arInOutJamEntr[1].nBarcode), L := 1, P := 1);
	                    //     #stEventNew.sDest := DELETE(IN := INT_TO_STRING(#arInOutJamEntr[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)
	                    #arInOutJamEntr[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
	                    #arInOutJamEntr[1].nBarcode <> #nBarcodeResult AND //Barcodes stimmen nicht überein, erwarteter Carrier enthält aber normalen Barcode
	                    #arInOutJamEntr[1].nBarcode <> 0 AND
	                    #arInOutJamEntr[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 := #arInOutJamEntr[1]);
	                    // END_REGION
	                    
	                    // IF NOT #xTemp THEN
	                    //     ;
	                    //     // #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    // ELSE
	                    //     #arInOutJamEntr[1].nDestination := "cDestNONE"; //neuer Carrier // TODO
	                    //     //     #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                    //     #arInOutJamEntr[1].nBarcode := #nBarcodeResult; //Hier werden neue Carrier im System angelegt
	                    //     #arInOutJamEntr[1].bStatus.%X0 := TRUE;
	                    // END_IF;
	                    
	                    // REGION Event Log New Carrier
	                    
	                    //     #xNewCarrier := TRUE; // Trigger
	                    //     #stEventNew.nScanType := 2;
	                    //     #stEventNew.sBarcode := DELETE(IN := DINT_TO_STRING(#arInOutJamEntr[1].nBarcode), L := 1, P := 1);
	                    //     #stEventNew.sDest := DELETE(IN := INT_TO_STRING(#arInOutJamEntr[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!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	                    #arInOutJamEntr[1].bStatus.%X0 := TRUE;
	                    #arInOutJamEntr[1].nBarcode := 2; //unbekannter Carrier mit Badread, erwartet 0 und physikalisch vorhanden
	                    #arInOutJamEntr[1].nDestination := 123; //todo
	                    
	                    REGION Event Log unknown Carrier
	                        
	                        #xUnknownCarrier := TRUE; // Trigger
	                        #stEventUnknown.nScanType := 3;
	                        #stEventUnknown.sBarcode := DELETE(IN := DINT_TO_STRING(#arInOutJamEntr[1].nBarcode), L := 1, P := 1);
	                        #stEventUnknown.sDest := DELETE(IN := INT_TO_STRING(#arInOutJamEntr[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 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 ////////
	                #tmpActUnivTon := TRUE;
	                IF (#arInOutJamEntr[1].bStatus.%X1 AND #xTrolleyWithPart) OR //Daten und physikalisch -> beladen Ok!
	                    (NOT #arInOutJamEntr[1].bStatus.%X1 AND #xTrolleyWoPart) //Daten und physikalisch -> unbeladen Ok!
	                THEN
	                    #nState := #c1400_StepCheckSep1; // Weiter
	                ELSIF #fbStateManager.tOutUnivTonR > T#3s THEN
	                    #tmpReasonForStop := 123; // TODO: Beladekontrolle Fehler
	                END_IF;
	                
	            END_REGION
	            
	        #c1400_StepCheckSep1:
	            REGION 1400: Check Separator                
	                // akt. Zustand
	                #sStateText := 'Check Sep';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                #xOutNewScan := FALSE;
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                #xVarioReq := TRUE;
	                #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 //////////
	                #stOutPriorityManager.xTransportReq := FALSE; // Beim ersten Bein noch nicht nötig
	                
	                #xReleaseTrolleySep1 :=
	                (NOT #fbSepWait.xOutLockSep1 AND (* #xInCarouselRun AND *)#xInSftyOk) AND // Erste Achse darf vereinzelt werden
	                (NOT #stInSettings.bSepType.%X0 OR
	                (#xInRelease AND #stInSettings.bSepType.%X0))
	                ;
	                
	                //////// 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;
	                #xOutNewScan := 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
	                        #arInOutJamEntr[1].bStatus.%X0 // Carrier ist online
	                        AND (#xInRelease AND NOT #stInSettings.bSepType.%X0) OR #stInSettings.bSepType.%X0
	                    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 := #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 #fbBarcodeReader.xOutReadyForNewScan THEN
	                    #xStartScan := TRUE;
	                END_IF;
	                
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                
	                IF #fbBarcodeReader.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 #fbBarcodeReader.xOutCodeGood THEN
	                    #nBarcodeResult := LINT_TO_DINT(#fbBarcodeReader.#nOutBarcode); // Good read vom Barcode reader
	                ELSIF #fbBarcodeReader.#xOutCodeBad THEN
	                    #nBarcodeResult := #arInOutJamEntr[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)
	                    (#arInOutJamEntr[1].nBarcode = 0 OR #arInOutJamEntr[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
	                        #arInOutJamEntr[1].nDestination := "cDestNONE"; //neuer Carrier // TODO
	                        //     #stInOutCarrier.stCarrData.nNrOfScans += 1;
	                        #arInOutJamEntr[1].nBarcode := #nBarcodeResult; //Hier werden neue Carrier im System angelegt
	                        #arInOutJamEntr[1].bStatus.%X0 := TRUE;
	                    END_IF;
	                ELSIF
	                    (#nBarcodeResult > 100) AND
	                    (#nBarcodeResult < 2147483647) AND // Good Read (2.147.483.647 Maximum von DINT)
	                    #arInOutJamEntr[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
	                    #arInOutJamEntr[1].nBarcode <> #nBarcodeResult AND //Barcodes stimmen nicht überein, erwarteter Carrier enthält aber normalen Barcode
	                    #arInOutJamEntr[1].nBarcode <> 0 AND
	                    #arInOutJamEntr[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!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	                    #arInOutJamEntr[1].bStatus.%X0 := TRUE;
	                    #arInOutJamEntr[1].nBarcode := 2; //unbekannter Carrier mit Badread, erwartet 0 und physikalisch vorhanden
	                    #arInOutJamEntr[1].nDestination := 123; //todo
	                END_IF;
	                
	                #xHandshakeScan := TRUE;
	                
	                /////////  Timer  //////////
	                
	                //////// Transition ////////
	                #nState := #c2300_StepCheckSep2;
	                
	            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 := #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 (#stInOutJamFinger.Status.nCount = #stInOutJamFinger.Config.nCapacitySet) //Zielstaubereich ist datentechnisch voll
	                THEN
	                    #tmpReasonForStop := "Rsn_JamNextError";
	                ELSE
	                    #nState := #c2500_StepWaitForConvRdy;
	                END_IF;
	                
	            END_REGION
	            
	        #c2500_StepWaitForConvRdy:
	            REGION 2500: Wait for Finger Conveyor
	                // akt. Zustand
	                #sStateText := 'Wait Finger Conv';
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                #xReleaseTrolleySep1 := FALSE;
	                //  #xVarioReq := TRUE; // Anforderung an Fingerförderer
	                
	                /////////  Timer  //////////
	                //////// Transition ////////
	                IF #fbConvVario.xOutConveyorReady THEN
	                    #nState := #c2600_StepReleaseSep2;
	                END_IF;
	            END_REGION
	            
	        #c2600_StepReleaseSep2:
	            REGION 2600: Leave Sep
	                
	                // akt. Zustand
	                #sStateText := 'Leave Sep';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                
	                #xReleaseTrolleySep1 := #xInSftyOk AND #fbConvVario.xOutConveyorReady (* AND NOT #stInMfr.xPause *)AND
	                #stInOutJamFinger.Status.nCount < #stInOutJamFinger.Config.nCapacitySet AND
	                NOT #stInOutJamFinger.Status.bByte.%X2 AND NOT #stInOutJamEntr.Status.bByte.%X3 AND
	                NOT #fbSepWait.xOutLockSep1;
	                #xVarioTrigger := FALSE;
	                /////////  Timer  //////////
	                
	                // Reason for Stop
	                (* IF #stInMfr.xPause THEN
	                    #tmpReasonForStop := "Rsn_MfrPausedTro";
	                END_IF; *)
	                
	                IF NOT #fbConvVario.xOutConveyorReady THEN
	                    #tmpReasonForStop := "Rsn_CarouselError";
	                END_IF;
	                
	                //////// Transition ////////    
	                IF #fbSeparator1.stOutMainTro.nStatus = "SepState_Finish" THEN
	                    
	                    // REGION Buchen Entry -> Finger
	                    //         "FC_Route_1"(xInSingle := FALSE,
	                    //                    xInSeparate := FALSE,
	                    //                    xInSepEdge := TRUE,
	                    //                    xInSensor := FALSE,
	                    //                    xInDelay := FALSE,
	                    //                    stInCarrier := #stEmptyCarrier,
	                    //                    stOutBookingInfo => #stBookingInfo,
	                    //                    arJamEntr := #arInOutJamEntr,
	                    //                    arOnTheWay := #arEmptyCarrier,
	                    //                    arJamExit := #arInOutJamFinger);
	                    // END_REGION
	                    
	                    // #xVarioTrigger := TRUE;
	                    // #xEventLogRoute := TRUE;
	                    //   #nState := #c1000_StepWaitForTrolley1;
	                    //NEU!!!!!!!!!!
	                    #xLastPosReached := TRUE;
	                    
	                    #nState := #c2700_WaitForCarrInPos;
	                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
	            
	        #c2700_WaitForCarrInPos:
	            REGION 2700: Wait for Sensor "In Pos"
	                
	                // akt. Zustand
	                #sStateText := 'Wait for Sensor "In Pos"';
	                
	                ///////// Ausgänge /////////
	                ///////// Statics //////////
	                
	                #xReleaseTrolleySep1 := FALSE;
	                /////////  Timer  //////////
	                
	                IF NOT #fbConvVario.xOutConveyorReady THEN
	                    #tmpReasonForStop := "Rsn_CarouselError";
	                END_IF;
	                
	                //////// Transition ////////    
	                
	                IF (#stInVario.stSenCarrInPos.xDbncdSensor AND (#stInVario.stSenCarrInPos.tDirectChangeLast > #stInVario.Settings.tCarrierInPosDelay)) OR //Timer ggf. umbennen, wird ansonsten aktuell nirgendwo verwendet
	                    //#FTrig_CarrInPos.Q
	                    #xCarrierSensorPass
	                THEN
	                // IF (#stInVario.stSenCarrInPos.xDbncdSensor AND #stInVario.stSenCarrInPos.tDirectChangeLast > T#1s AND #fbStateManager.tOutTimeInStep > T#1s) OR // Sensor belegt + Mindestzeit vergangen
	                //     (#fbStateManager.tOutTimeInStep > t#3s) // Timeout
	               // THEN
	                    #stTempCarrier := #arInOutJamEntr[1];
	                    
	                    REGION Buchen Entry -> Finger
	                        "FC_Route"(xInSingle := FALSE,
	                                   xInSeparate := FALSE,
	                                   xInSepEdge := TRUE,
	                                   xInSensor := FALSE,
	                                   xInDelay := FALSE,
	                                   stInCarrier := #stEmptyCarrier,
	                                   stOutBookingInfo => #stBookingInfo,
	                                   arJamEntr := #arInOutJamEntr,
	                                   arOnTheWay := #arEmptyCarrier,
	                                   arJamExit := #arInOutJamFinger);
	                    END_REGION
	                    
	                    #xCarrierSensorPass := FALSE;
	                    
	                    #xBooked := TRUE;
	                    
	                    #xLastPosReached := FALSE;
	                    
	                    #xVarioTrigger := TRUE;
	                    #xEventLogRoute := TRUE;
	                    #nState := #c1000_StepWaitForTrolley1;
	                ELSIF #fbStateManager.tOutTimeInStep > #stInVario.Settings.tTimeOutCarrierInPos AND NOT #stInVario.stSenCarrInPos.xDirectSensor AND #stInVario.stSenCarrInPos.tDirectChangeLast > #stInVario.Settings.tTimeOutCarrierInPos THEN  // Wenn wir hier stecken bleiben, könnte man eine Korrektur versuchen... z.B. zurück zu nState = 2000
	                    #nState := #c2000_StepWaitForTrolley2;
	                    #tmpReasonForStop := "Rsn_SubError";
	                    #xBtEmpty := TRUE; //provisorisch
	                END_IF;
	                    
	                // //ELSIF #fbStateManager.tOutTimeInStep > T#5s THEN
	                //     #tmpReasonForStop := "Rsn_SubError";
	                //     #xBtEmpty := TRUE; //provisorisch
	                // END_IF;
	            END_REGION ;
	            
	            
	(*        // "VarioStep_CheckVario":
	        //     REGION 2550: Check Vario
	                
	        //         // akt. Zustand
	        //         #sStateText := 'Check Vario';
	                
	        //         ///////// Ausgänge /////////
	        //         ///////// Statics //////////
	                
	        //         #xReleaseTrolleySep1 := FALSE;
	        //         /////////  Timer  //////////
	                
	        //         // Reason for Stop
	                
	        //         //////// Transition ////////    
	        //         IF NOT #arInOutJamExit[#nArrayMaxExit].bStatus.%X0 AND NOT #stInVario.SenJam.xDbncdSensor AND
	        //             #xLastPosReached AND NOT #stInMfr.xPause THEN //darf takten wenn letzte Position erreicht und nachfolgender Bereich frei ist
	        //             #xLastPosReached := FALSE;
	        //             #xVarioTrigger := TRUE;
	        //             #nState := "VarioStep_VarioMove";
	        //             //vorderstes Teil (LastPos) wird in Exit-Jam geschoben
	        //             REGION Buchen Finger -> Exit
	        //                 "FC_Route"(xInSingle := FALSE,
	        //                            xInSeparate := FALSE,
	        //                            xInSepEdge := TRUE,
	        //                            xInSensor := FALSE,
	        //                            xInDelay := FALSE,
	        //                            stInCarrier := #stEmptyCarrier,
	        //                            stOutBookingInfo => #stBookingInfo,
	        //                            arJamEntr := #arInOutJamFinger,
	        //                            arOnTheWay := #arEmptyCarrier,
	        //                            arJamExit := #arInOutJamExit);
	        //             END_REGION
	
	        //         ELSIF NOT #xLastPosReached AND NOT #stInMfr.xPause THEN //darf takten wenn letzte Position zuvor noch nicht erreicht wurde
	        //             REGION Takten ohne Buchen
	        //                 #xLastPosReached := FALSE;
	        //                 #xVarioTrigger := TRUE;
	        //                 #nState := "VarioStep_VarioMove";
	        //             END_REGION
	
	        //         END_IF;
	                
	        //     END_REGION
	            
	        // "VarioStep_VarioMove":
	        //     REGION 2575: Vario Move
	                
	        //         // akt. Zustand
	        //         #sStateText := 'Vario Move';
	                
	        //         ///////// Ausgänge /////////
	        //         ///////// Statics //////////
	                
	        //         #xReleaseTrolleySep1 := FALSE;
	        //         #xVarioTrigger := FALSE;
	        //         /////////  Timer  //////////
	                
	                
	        //         //////// Transition ////////    
	                
	        //         IF #stInVario.SenLastPos.xDirectEdgePos AND NOT #tOfLastPos.Q THEN
	        //             #xLastPosReached := TRUE;
	        //         END_IF;
	                
	        //         IF #fbConvVario.xOutConveyorReady THEN
	        //             #nState := "VarioStep_WaitForMfr";
	        //         END_IF;
	                
	        //     END_REGION *)
	            
	    END_CASE;
	    
	END_REGION
	
	REGION Timer - Überwachungszeit
	   
	    REGION Separator
	        #tOnSvReleaseSep(IN := (#nState = #c1500_StepReleaseSep1 OR #nState = #c2600_StepReleaseSep2) AND #xReleaseTrolleySep1,
	                         PT := T#3s);
	    END_REGION
	    
	    REGION Carrier in Pos
	        #tOfLastPos(IN := ((#nState = #c2600_StepReleaseSep2) OR (#nState = #c2700_WaitForCarrInPos)) AND #stInVario.stSenCarrInPos.xDirectEdgePos,
	                    PT := #stInVario.Settings.tCarrierInPosEdgeDelay); //z.B.
	        
	        #FTrig_CarrInPos(CLK := #tOfLastPos.Q);
	        
	        IF #FTrig_CarrInPos.Q THEN
	            #xCarrierSensorPass := true;//#FTrig_CarrInPos.Q;
	        END_IF;
	    END_REGION
	
	END_REGION
	
	REGION Haltegrund Schrittkette    
	    #stActRequest.nReasonForStop := #tmpReasonForStop;  //tmp aus Schrittkette übernehmen
	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 ((#arInOutJamEntr[1].bStatus.%X1 AND #xTrolleyWoPart) OR
	            (NOT #arInOutJamEntr[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 := #arInOutJamEntr, DIM := 1));
	    
	    REGION Dummy erstellen
	        IF #stInSettings.xIsBorner THEN
	            IF #nBarcodeEmpty > 1999 OR #nBarcodeEmpty < 1000 THEN
	                #nBarcodeEmpty := 1000;
	            ELSE
	                IF #nState = 2600 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
	                #arInOutJamEntr[1] = #tmpCarrierEmpty
	            THEN
	                #arInOutJamEntr[1].nBarcode := #nBarcodeEmpty;
	                #arInOutJamEntr[1].bStatus.%X0 := TRUE;
	                #arInOutJamEntr[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?
	                #arInOutJamEntr[1] = #tmpCarrierEmpty
	            THEN
	                #arInOutJamEntr[1].nBarcode := #cCarrierDummy;
	                #arInOutJamEntr[1].bStatus.%X0 := TRUE;
	            END_IF;
	            
	        END_IF;
	        
	    END_REGION
	    
	    REGION Dummy löschen - ÜBERARBEITEN!
	        IF #nArrayMax > 1 THEN //Wenn ein einzelner Dummy oder Barcode 2 vorhanden ohne Belegung nach 2 Min löschen (Ausnahme in Qualität)
	            IF (#arInOutJamEntr[1] = #stCarrierDummy)
	                AND #arInOutJamEntr[2] = #tmpCarrierEmpty AND
	                NOT #stInSeparator1.stSenInSep.xDirectSensor AND #stInSeparator1.stSenInSep.tDirectChangeLast > T#8s AND
	                #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForBt" AND #fbSeparator1.#fbStateManager.tOutTimeInStep > T#8s
	            THEN
	                #arInOutJamEntr[1] := #tmpCarrierEmpty;
	            ELSIF (#arInOutJamEntr[1] = #stCarrierDummy)
	                AND (#arInOutJamEntr[2] = #stCarrierDummy OR #arInOutJamEntr[2] <> #stCarrierDummy) AND
	                NOT #stInSeparator1.stSenInSep.xDirectSensor AND #stInSeparator1.stSenInSep.tDirectChangeLast > T#8s AND
	                #fbSeparator1.stOutMainTro.nStatus = "SepState_WaitForBt" AND #fbSeparator1.#fbStateManager.tOutTimeInStep > T#8s
	            THEN
	                #arInOutJamEntr[1] := #tmpCarrierEmpty;
	                "FC_TSHR_Shift"(xInShiftBack := TRUE,
	                                arTSHR := #arInOutJamEntr);
	            END_IF;
	        ELSIF #nArrayMax = 1 THEN
	            IF (#arInOutJamEntr[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
	            THEN
	                #arInOutJamEntr[1] := #tmpCarrierEmpty;
	            END_IF;
	        END_IF;
	        
	    END_REGION
	    
	END_REGION
	
	REGION Monitoring
	    
	    REGION Carrier kommt nicht an / Daten ohne Belegung
	        #tOnMonCarrArrive(IN := #arInOutJamEntr[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(sInFctnName := '',
	                                   wInTextListId_Function := 1000,
	                                   wInTextListId_Message := 1000,
	                                   sInPrefix := #stInOutHMI.UDT_Config.sName,
	                                   sInSuffix := #stInOutHMI.UDT_Separator1.stConfig.sName,
	                                   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 #arInOutJamEntr[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(sInFctnName := '',
	                                wInTextListId_Function := 1000,
	                                wInTextListId_Message := 1001,
	                                sInPrefix := #stInOutHMI.UDT_Config.sName,
	                                sInSuffix := #stInOutHMI.UDT_Separator1.stConfig.sName,
	                                InOutMachineState := #stInOutMachineState,
	                                xInQuitError := #stInOutMachineState.xQuitError,
	                                xInSignal := NOT #wErrorWord.%X5,
	                                sInAlarmMessage := 'Sep-Belegung ohne Daten');
	    END_REGION
	    
	    #fbAlarmVarioMTinPos.stSettings.nNoMaxErrors := 1;
	    #fbAlarmVarioMTinPos.stSettings.nType := 2;
	    #fbAlarmVarioMTinPos.stSettings.sFctnName := '';
	    #fbAlarmVarioMTinPos(xInAlarm := #xBtEmpty,
	                         nInAlarmNumber := 0,
	                         sInPrefix := 'MT in Pos',
	                         sInSuffix := '',
	                         wInTextListId_Message := 300,
	                         wInTextListId_Function := 300,
	                         stInOutControlUnitCabinet := #stInOutMachineState);
	    
	END_REGION
	
	
	REGION Motorüberwachung - todo
	    IF #nState = 40000 AND #fbStateManager.tOutTimeInStep > T#4s THEN
	        #xErrorMotor := TRUE;
	    ELSE
	        #xErrorMotor := FALSE;
	    END_IF;
	END_REGION
	
	REGION Fahrbedingungen für Vario
	    
	    // IF #arInOutJamExit[1].bStatus.%X0 AND
	    //     NOT #stInVario.SenJam.xDirectSensor AND #stInVario.SenJam.tDirectChangeLast > #stInVario.Settings.tFreeJam AND
	    //     NOT #stInVario.SenNextSep.xDirectSensor AND #stInVario.SenNextSep.tDirectChangeLast > #stInVario.Settings.tFreeNextSep AND
	    //     NOT #stInVario.SenSepVario.xDirectSensor AND #stInVario.SenSepVario.tDirectChangeLast > #stInVario.Settings.tFreeVarioSep THEN
	    //     #xVarioRelease := TRUE;
	    // ELSE
	    //     #xVarioRelease := FALSE;
	    // END_IF;
	    
	END_REGION
	
	REGION Wartezeit Separator
	    
	    #fbSepWait.stInTimes.tFreeSen1 := #stInSeparator1.Settings.tSenFree;
	    #fbSepWait.stInTimes.tTimerWait := #stInSeparator1.Settings.tSenWait;
	    #fbSepWait(stInSensor1 := #stInSeparator1.stSenInSep,
	               xInConvActive := TRUE);
	    
	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 #xInReset,
	                    xInRunUnivTOn := #tmpActUnivTon,
	                    xInTestMode := #xInTestMode,
	                    nInOutState := #nState);
	    
	END_REGION
	
	REGION Aufruf der Sub-TROs
	    
	    REGION Separator 1
	        
	        #fbSeparator1(xInReset := #stInOutHMI.UDT_Ctrl.xReset OR #xInReset,
	                      xInErrQuit := #stInOutHMI.UDT_Ctrl.xErrorQuit OR #xInErrorQuit,
	                      stInOutMainState := #stInOutMachineState,
	                      stInSettings := #stInSeparator1.Settings,
	                      xInConveyorRun := TRUE, //Gefälle...
	                      stInTrolley := #stInSeparator1.stSenInSep,
	                      xInReceiveReady := #xReleaseTrolleySep1,
	                      stInOutHMI := #stInOutHMI.UDT_Separator1,
	                      stInOutCarrDataInJam := #arInOutJamEntr);
	        
	        #stInOutHMI.UDT_Separator1.stStatus.xNullBt := #stInOutHMI.UDT_Ctrl.xPause;
	        #stInOutHMI.UDT_Separator2.stStatus.xNullBt := #stInOutHMI.UDT_Ctrl.xPause;
	    END_REGION
	    
	    REGION Vario Förderer
	        
	      
	        
	        
	        
	        #fbConvVario(stInSettings := #stInVario.Settings,
	                     xInAutomatic := #xInAutomatic,
	                     xInErrorQuit := #xInErrorQuit OR #stInOutHMI.UDT_Ctrl.xErrorQuit,
	                     xInReleaseEdge := #xVarioTrigger, // Separator Release Edge
	                     stInSenConvFingerInPos := #stInVario.stSenFinger,
	                     stInSenJam := #stInVario.stSenJam,
	                     stInSenLastPos := #stInVario.stSenLastPos,
	                     stInSenCarrInPos := #stInVario.stSenCarrInPos,
	                     xInRequest := #xVarioReq AND (NOT #fbSepWait.xOutLockSep1 OR #xLastPosReached),//#xVarioReq AND NOT #fbSepWait.xOutLockSep1 AND NOT #stInOutHMI.UDT_Separator1.stControl.xLockSeparator,
	                     xInMotorProtection := #xInMotorProtection,
	                     stInStationSensor:= #stInStationSensor,// TEST 
	                     xInReleaseStation:= #xInReleaseStation,// TEST
	                     xOutMotor => #xOutVarioMotor,
	                     stInOutControlUnitCabinet := #stInOutMachineState,
	                     stInOutHMI := #stInOutHMI.UDT_Vario1,
	                     arInOutJamFinger := #arInOutJamFinger);
	        
	        
	        
	    END_REGION
	    
	    REGION Barcode Reader 1
	        #fbBarcodeReader(stInSettings := #stInBarcodeReader.Settings,
	                          xInReset := #stInOutHMI.UDT_Ctrl.xReset,
	                          xInHWError := false, // todo
	                          xInStartReading := #xStartScan,
	                          xInHandshakeDone := #xHandshakeScan,
	                          stInOutInterface := #stInOutCognexInterface,
	                          stInOutControlUnitCabinet := #stInOutMachineState,
	                          stInOutHMI := #stInOutHMI.UDT_BarcodeReader2);
	    END_REGION
	    
	END_REGION
	
	REGION BT zählen - NIU
	    
	    CASE #stActRequest.nDirection OF
	        0:
	            #stActRequest.nActNoStopper := 0;
	        1:
	            #stActRequest.nActNoStopper := UDINT_TO_USINT(#fbSeparator1.stOutMainTro.nCounterBt - #stActRequest.nCntAtStartSep1);
	    END_CASE;
	    
	    
	END_REGION
	
	REGION HMI
	    #stInOutHMI.UDT_Config.nTroType := 5; //Vario
	    
	    #stInOutHMI.UDT_State.nState := #nState;
	    #stInOutHMI.UDT_State.sState := #sStateText;
	    
	    #stInOutHMI.UDT_State.xError :=
	    #fbSeparator1.wOutErrorWord > 0 OR
	    #stInOutHMI.UDT_Separator1.stStatus.bStatus = 2;
	    //usw.
	    
	END_REGION
	
	REGION Ausgänge schreiben
	    #xOutBooked := #xBooked;
	    #stOutLastCarrier := #stTempCarrier;
	    
	    REGION Output
	        
	        #xOutStopper := #fbSeparator1.xOutStopper;
	        #xOutJamAtEntry1 := #fbSeparator1.stOutMainTro.xJamAtEntry;
	        #xOutCorrection := #xTmpCorrection;
	        // #xOutCarouselReq := ((#nState >= "VarioStep_CheckCorrectBt" AND #nState <= "VarioStep_LeaveSep")) (* AND
	        // NOT #stInMfr.xPause *);
	        
	    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]);
	    
	END_REGION
	
	#fbEventLogCarrierUnknown(xInTrigger := #xUnknownCarrier,
	                          stInEventScanInfo := #stEventUnknown);
	
	#fbEventLogCarrierNew(xInTrigger := #xNewCarrier,
	                      stInEventScanInfo := #stEventNew);
	
	#fbEventLogGoodRead(xInTrigger := #xGoodRead,
	                    stInEventScanInfo := #stEventGoodRead);
	
	REGION Letzten Schritt wegschreiben
	    #nOutStateLast := #fbStateManager.nStateLast;
	END_REGION
END_FUNCTION_BLOCK

