FUNCTION_BLOCK "MHMOmniSchleuse"
TITLE = HM-Omni-Schleuse (Schrittkette)
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : IBAE
FAMILY : Module
NAME : MHMOmni
//SCL-Portierung des AWL-Bausteins FB1274 "MHMOmniSchleuse" (verhaltensgleich).
//Statische Daten (SAVE/Para/Status2/Schleuse/Dyn) sind unveraendert -> Instanz-DBs bleiben gueltig.
//
//ABWEICHUNGEN ZUM AWL-ORIGINAL (bei Inbetriebnahme pruefen!):
//  - DatenOut-Aufrufe verwenden lokale Puffer statt der Merker-Scratch-Bereiche
//    MW200/M220../MB199/MW196 (Ergebnis identisch, keine Nebenwirkung auf Merker).
//    Die Merker-Schnittstelle zu den indirekten FCs (MW190..MW198, MD182, MD192,
//    M180.x) und die Fehler-Schnittstelle MW200/M200.x/M201.x bleiben erhalten.
//  - Erfolg von "DatenOut" wird ueber RE_VAL Bit7 statt BR/JBI erkannt
//    (bei PARA=0-Suchen aequivalent).
//  - Indirekte Aufrufe UC FC[LeserFC/ZielFC/DraussenFC] sind als CASE ueber die
//    projektierten FC-Nummern ausgefuehrt (Station UH06).
//  - Registerindirekte Zugriffe auf Weichen-/Leser-DB sind durch PEEK/POKE ersetzt.
//  - NW21 ("Schrittkette anhalten") ist im Original durch CLR+JU deaktiviert -> entfaellt.
//  - NW10-Schritt "NoLesung": das AWL verglich HMI.TrolleyID_Input mit einem
//    undefinierten Akku-Altwert; hier wird die beabsichtigte Pruefung
//    (TrolleyID_Input <> 0) verwendet. VERHALTEN PRUEFEN!
   VAR_INPUT
      Schleusen_Nr : Int;   // Aufruf fuer Schleuse
      ErrQuit : Bool;   // Ablaufstoerung quittieren
      B_VorSchleuse : Bool;   // Sensor Trolley faehrt in Bereich
      B_InSchleuse : Bool;   // Sensor Trolley ist in Schleuse
      B_AusSchleuse : Bool;   // Sensor Trolley hat Schleuse verlassen
      B_Stau_raus : Bool;   // Sensor Stau im Bereich rausfahren
      Start_Ok : Bool;   // Starten ist erlaubt
      Antrieb_Schleuse_aktiv : Bool;   // Kreisel-Antrieb aktiv
      Antrieb_plus_aktiv : Bool;   // Kreisel-Antrieb plus aktiv
      "Schleuse_geöffnet" : Bool;   // Schleuse hat geoeffnet
      RestZeit_Bereich_plus : Int := 999;   // Bereich +1: Restzeit bis AFS stoppt (s)
   END_VAR

   VAR_OUTPUT
      Y_Schleuse_Auf : Bool;   // Schleuse oeffnen
      Error : Word;   // Stoerungen uebergeben
   END_VAR

   VAR_IN_OUT
      Antrieb_Schleuse_vor : Bool;   // Schleusen-Antrieb einschalten
      Antrieb_Schleuse_start : Bool;   // Schleusen-Antrieb mit Zeit starten
      Antrieb_Schleuse_stop : Bool;   // Schleusen-Antrieb stoppen
      Antrieb_Schleuse_PS : Bool;   // Schleusen-Antrieb Energie sparen
      Antrieb_plus_vor : Bool;   // Antrieb plus einschalten
      Antrieb_plus_start : Bool;   // Antrieb plus mit Zeit starten
      hStOk : Bool;   // Anzeige Start n. Stoerung aktivieren
   END_VAR

   VAR
      SAVE : Struct
         Weichen_DB : Int;   // Weichen-FIFO-DB
         Weichen_DW : Int;   // Weichen-FIFO-DW
         "Weiche+1" : Int;   // Weiche +1, wo reserviert wurde
         Frei18 : Int;
         Frei20 : Int;
      END_STRUCT;
      Para : Struct   // Parameter
         Schleusen_DL_DB : Int;   // Einsprung Schleuse-Datenliste-DB
         Weichen_DL_DB : Int;   // Einsprung Weichen-Datenliste-DB
         Leser_DL_DB : Int;   // Einsprung Leser-Datenliste-DB
      END_STRUCT;
      Status2 : Struct
         Trolley_an_Schleuse : Bool;   // Trolley ist an Schleuse
         "Warten_3.V" : Bool;   // Warten auf 3.V
         "2.Lesung_aktiv" : Bool;   // 2.Lesung erfolgt
         "3.Lesung_aktiv" : Bool;   // 3.Lesung erfolgt
         "TrolleyPos_übergeben" : Bool;   // Trolley-Pos. uebergeben
         "Warten_4.V" : Bool;   // Warten auf 4.V
         Ausfahrzeit_lernen : Bool;   // Ausfahrzeit lernen
         "Weichen-Schritt" : Bool;   // Schritt 13-17 aktiv (Weiche evtl. belegen)
         Trolley_ist_draussen : Bool;   // Rueckmeldung von Sensor / Weiche
         Einfahrzeit_lernen : Bool;   // Einfahrzeit lernen
         Telegramm_gesendet : Bool;   // Telegramm wurde in Send-FIFO eingetragen
      END_STRUCT;
      Schleuse : "UDLHMOmniSchleuse";   // Statische Schleusendaten (aus DL)
      Dyn : Struct   // Dynamische Daten
         Status : Struct
            "Trolley_fährt_in_Schleuse" : Bool;   // Trolley faehrt in Schleuse
            Trolley_wartet_Lesung : Bool;   // Trolley wartet auf Auftrag
            "Trolley_wartet_auf_Ziel-FC" : Bool;
            "Trolley_wartet_auf_Weiche+1" : Bool;
            "Trolley_wartet_Bereich+1" : Bool;   // Trolley wartet auf Bereich +1
            "Trolley_fährt_aus" : Bool;   // Trolley faehrt aus
            MindestZeit_ok : Bool;   // Schleuse ist MindestZeit geoeffnet
            Trolley_draussen : Bool;   // Trolley ist ausgefahren
            Leser_triggern : Bool;
            SF_angefordert : Bool;
            Kein_Stop_mehr : Bool;   // Kein Stop mehr, Antrieb soll starten
            "Trolley_fährt_rückwärts" : Bool;   // Trolley in Schleuse rueckwaerts erkannt
            Weiche_geschaltet : Bool;
            "Trolley ausserhalb Weiche" : Bool;   // Trolley hat Weiche verlassen (von Weiche)
            Error_aktiv : Bool;   // Stoerung vorhanden
            Reset_aktiv : Bool := TRUE;   // Reset aktiv
         END_STRUCT;
         TrolleyID_Schleuse : DInt;   // Trolley-Nr in der Schleuse
         Trolley_EndZiel : Word;   // Trolley-EndZiel
         Trolley_ZwischenZiel : Word;   // Trolley-ZwischenZiel
         Trolley_ShopGroup : DInt;   // Trolley-ShopGroup
         Weichen_Ziel : Word;   // Weichen-Ziel (fuer WeichenModul von Ziel-FC)
         TrolleyID_Eingefahren : DInt;   // Trolley-Nr von Bereich -1 gemeldet
         TrolleyID_ausgefahren : DInt;   // TrolleyID des ausgefahrenen Trolleys
         HMI : Struct
            TrolleyID_in_Schleuse : DInt;   // Trolley-Nr in Schleuse (IN:PLC, OUT:PLC)
            TrolleyID_Input : DInt;   // Trolley-Nr Input (IN:HMI, OUT:PLC)
            Schleuse_resetten : Bool;   // Schleuse resetten (IN:HMI, Out:PLC)
         END_STRUCT;
         Sk : "USk_ILS";   // Status der Schrittkette
         Leser : Struct
            "IN" : Struct
               xOutReadyForNewScan : Bool;   // DBX10.1: Reader ist bereit fuer Leseanforderung
               xOutReadingDone : Bool;   // DBX10.0: Lesevorgang abgeschlossen / Handshake raus
               xOutCodeGood : Bool;   // DBX278.0: Gute Lesung
               xOutCodeBad : Bool;   // DBX278.1: Schlechte Lesung
               nOutBarcode : DInt;   // DBD12: Trolley ID
            END_STRUCT;
            "OUT" : Struct
               xInStartReading : Bool;   // DBX4.1: Starte Lesevorgang
               xInHandshakeDone : Bool;   // DBX4.2: Handshake Daten abgeholt
               xInErrQuit : Bool;   // DBX4.4: Fehler ruecksetzen
               xInReset : Bool;   // DBX4.5: Allgemeines Ruecksetzen des Bausteins
            END_STRUCT;
         END_STRUCT;
         TrolleyID_unbekannt_Schleuse : DInt;   // TrolleyID unbekannt (Trolley in Schleuse)
         Rueckmld : Struct
            RET_VAL_BLKMOV_Schleuse : Int;   // Rueckmeldung nach BLKMOV-Aufruf
            RET_VAL_DL_Schleuse : Byte;
            RET_VAL_DL_Weiche : Byte;   // Rueckmeldung nach DatenOut-Aufruf
            RET_VAL_DL_Leser : Byte;   // Rueckmeldung nach DatenOut-Aufruf
         END_STRUCT;
         Error : "UErrHMOmniSchleuse";
         Stau_Zeit : Int;   // Sensor B_VorSep ist belegt [100ms]
         Mindest_Zeit : Int;   // Sensor B_VorSep ist frei [100ms]
         Leer_Zeit : Int;
         Stop_Ursache : Word;   // Stop-Stelle: Nw + Position
         Vereinzelungstakte : DInt;   // Anzahl der getaetigten Vereinzelungstakte
         ZwischenZiel_eingefahren : Word;   // Trolley-ZwischenZiel von Bereich -1
         Ohne_B : Int;
         SAVE2 : Struct
            Leser_DB : Int;   // Leser-FIFO-DB
            Leser_DD : DInt;   // Leser-FIFO-DD
         END_STRUCT;
         WarteZeit_PS : Int;
         Restzeit_bis_Schleuse : Int;
         Res200 : Array[1..16] of Int;   // Reserve
      END_STRUCT;
   END_VAR

   VAR_TEMP
      Ueb_akt : Bool;   // Ueberwachungszeit aktivieren
      Weiter : Bool;   // Weiterschaltbedingung ist erfuellt
      Warte_neu : Bool;   // Wartezeit neu triggern
      Warte_stop : Bool;   // Wartezeit stoppen
      "Kein_Weichenbereich +1" : Bool;
      "Reservierung_Weiche+1_aktiv" : Bool;
      Leser_Zugriff : Bool;   // Leser-Zugriff vorhanden
      LeserFC_vorhanden : Bool;
      ZielFC_vorhanden : Bool;
      DraussenFC_vorhanden : Bool;
      "TrolleyID_Bereich-1" : Bool;   // Trolley-ID von Bereich -1 gemeldet
      Einfahrzeit_nicht_bekannt : Bool;
      LeserFC : Int;   // Leser-FC
      ZielFC : Int;   // Ziel-FC
      DraussenFC : Int;   // Draussen-FC
      tReVal : Byte;   // Rueckmeldung DatenOut (statt MB199)
      tPosition : Int;   // Position von DatenOut (statt MW196)
      tSuchInt : Int;   // Suchdaten 1 Wort (statt MW200)
      tSuchID : DInt;   // Suchdaten TrolleyID (statt MD220)
      tOutDaten : Array[0..19] of Word;   // OutDaten-Puffer (statt M220..)
      tByte : Byte;   // Byte-Zwischenwert (statt MB200-Scratch)
      tNeuSchritt : Int;   // Uebergabe an Neu_Mig
      tWartezeit : Int;   // Uebergabe an Plus (*10ms)
      tUebzeit : Int;   // Uebergabe an tueb (*1s)
      tWeichDb : DInt;   // Weichen-DB fuer PEEK/POKE
      tWeichBase : DInt;   // Weichen-DW Basisoffset
      tLeserDb : DInt;   // Leser-DB fuer PEEK/POKE
      tLeserBase : DInt;   // Leser-DD Basisoffset
   END_VAR

BEGIN
    REGION NW1: Parameter-Uebergabe
        #Para.Schleusen_DL_DB := 274;   // Einsprung Schleuse-DL (vor Aufruf uebergeben!)
        #Para.Weichen_DL_DB := 279;   // Einsprung Weichen-DL
        #Para.Leser_DL_DB := 273;   // Einsprung Leser-DL
    END_REGION

    REGION NW2: Anzeige OP
        IF #ErrQuit THEN
            #Dyn.Sk.Err_Uebzeit := FALSE;   // Ueberwachungszeit abgelaufen quittieren
        END_IF;

        IF NOT #Dyn.Sk.Bereich_ok THEN
            #Dyn.Sk.OP_Schritt := 252;   // Bereichsstop / Not-Aus
        ELSIF NOT #Dyn.Sk.Auto_akt AND NOT #Dyn.Sk.Hand_akt THEN
            #Dyn.Sk.OP_Schritt := 253;   // Keine Betriebsart
        ELSIF #Dyn.Sk.Hand_akt THEN
            #Dyn.Sk.OP_Schritt := 255;   // Handbetrieb aktiv
        ELSE
            #Dyn.Sk.OP_Schritt := #Dyn.Sk.Akt_Schritt;   // Aktiver Schritt
        END_IF;
    END_REGION

    REGION NW3: Out-Bits zum Zuruecksetzen (jeden Zyklus neu)
        #Y_Schleuse_Auf := FALSE;
        #Dyn.Status.Trolley_wartet_Lesung := FALSE;
        #Dyn.Status."Trolley_wartet_auf_Ziel-FC" := FALSE;
        #Dyn.Status."Trolley_wartet_auf_Weiche+1" := FALSE;
        #Dyn.Status."Trolley_wartet_Bereich+1" := FALSE;
        #Dyn.Status."Trolley_fährt_aus" := FALSE;
        #Dyn.Status.Trolley_draussen := FALSE;
        #Leser_Zugriff := FALSE;
        #Status2."Weichen-Schritt" := FALSE;

        #Dyn.Status.Reset_aktiv := #Dyn.Sk.Reset_Akt;
    END_REGION

    REGION NW4: HMI-Zeug
        IF #Dyn.HMI.Schleuse_resetten THEN   // von HMI
            #Dyn.HMI.Schleuse_resetten := FALSE;
            #Dyn.Sk.Reset := TRUE;   // SK zuruecksetzen
        END_IF;

        #Dyn.HMI.TrolleyID_in_Schleuse := #Dyn.TrolleyID_Schleuse;   // an HMI
    END_REGION

    REGION NW5: Schrittkette zuruecksetzen
        IF #ErrQuit THEN
            #Dyn.Sk.Err_Uebzeit := FALSE;
        END_IF;

        IF #Dyn.Sk.Reset THEN   // SK zuruecksetzen
            #Dyn.Sk.Akt_Schritt := 0;   // Schritt 0: Warten
            #Dyn.Sk.Terr_Zelle := 0;   // Zeitwerte loeschen
            #Dyn.Sk.Tok_Zelle := 0;

            #Dyn.Error.Err_BLKMov := FALSE;
            #Dyn.Sk.Reset := FALSE;
            #Dyn.Sk.Err_Schritt := FALSE;
            #Dyn.Sk.Err_Uebzeit := FALSE;
            #Status2.Telegramm_gesendet := FALSE;
            #Dyn.Sk.Reset_Akt := TRUE;   // Reset ist aktiv

            #Dyn.Error.Schleuse_DL := FALSE;
            #Dyn.Error.Undef_Schleuse := FALSE;
            #Dyn.Error.Err_Trolley_nicht_in_Schleuse := FALSE;
            #Dyn.Error.Err_Uebzeit := FALSE;
            #Dyn.Error.Err_Schritt := FALSE;
            #Dyn.Error.Err_Stau_Einfahrt := FALSE;
            #Dyn.Error.Err_Trolley_raus := FALSE;
            #Dyn.Error.Err_Raus_Sensor := FALSE;
            #Dyn.Error.Err_Ziel := FALSE;
            #Dyn.Error.Err_Weichen_DL := FALSE;
            #Dyn.Error.Err_Undef_Weiche := FALSE;
            #Dyn.Error.Err_Weiche_belegt := FALSE;
            #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
            #Dyn.Error.Err_Leser_nicht_bereit := FALSE;

            #Dyn.Leser."IN".nOutBarcode := 0;

            #Dyn.Stau_Zeit := 0;
            #Dyn.Mindest_Zeit := 0;
            #SAVE."Weiche+1" := 0;
            #SAVE.Weichen_DB := 0;
            #SAVE.Weichen_DW := 0;
            #Dyn.SAVE2.Leser_DB := 0;
            #Dyn.SAVE2.Leser_DD := 0;

            #Schleuse.Schleusen_Nr := 0;   // Damit statische Werte neu aus Datenbank geholt werden!

            GOTO mRET;
        END_IF;
    END_REGION

    REGION NW6: Stoerungen nach aussen uebergeben (Schnittstelle MW200)
        "MW200" := 16#0000;

        "M200.0" := #Dyn.Error.Schleuse_DL;   // Schleuse-Datenliste fehlerhaft
        "M200.1" := #Dyn.Error.Undef_Schleuse;   // undefinierte Schleuse
        "M200.2" := #Dyn.Error.Err_Trolley_nicht_in_Schleuse;   // Trolley faehrt nicht in Schleuse
        #Dyn.Error.Err_Uebzeit := #Dyn.Sk.Err_Uebzeit;   // Ueberwachungszeit ist abgelaufen
        "M200.3" := #Dyn.Sk.Err_Uebzeit;
        IF NOT #Dyn.Sk.Err_Uebzeit THEN   // AWL: NEG + R
            #Dyn.Sk.Err_Uebzeit_AnzOk := FALSE;
        END_IF;
        #Dyn.Error.Err_Schritt := #Dyn.Sk.Err_Schritt;   // Schritt-Nummer ist nicht definiert
        "M200.4" := #Dyn.Sk.Err_Schritt;
        "M200.5" := #Dyn.Error.Err_Stau_Einfahrt AND NOT #B_InSchleuse AND NOT #Y_Schleuse_Auf;
        "M200.6" := #Dyn.Error.Err_BLKMov;   // Error bei Block kopieren
        "M200.7" := #Dyn.Error.Err_Trolley_raus;   // Trolley faehrt nicht aus
        "M201.0" := #Dyn.Error.Err_Raus_Sensor;   // Raus-/Belade-Sensor wird nicht erreicht
        "M201.1" := #Dyn.Error.Err_Ziel;   // Trolley-Ziel nicht bekannt
        "M201.2" := #Dyn.Error.Err_Weichen_DL;   // Weichen-Datenliste fehlerhaft
        "M201.3" := #Dyn.Error.Err_Undef_Weiche;   // undefinierte Weiche
        "M201.4" := #Dyn.Error.Err_Weiche_belegt;   // Weiche bleibt belegt
        "M201.5" := #Dyn.Error.Err_Kein_LeseErgebnis;
        "M201.6" := #Dyn.Error.Err_Leser_nicht_bereit;
        "M201.7" := #Dyn.Error.Barcode_nicht_bekannt;

        // Andere Meldung als Ueberwachungszeit aktiv?
        IF (("MW200" AND 2#1111_0111_1111_1111) <> 16#0000)
           OR NOT #Dyn.Sk.Err_Uebzeit_AnzOk THEN
            "M200.3" := FALSE;   // Ueb.Zeit-Meldung nicht an PC senden
        END_IF;

        IF #Dyn.Sk.Err_Uebzeit THEN
            #Dyn.Sk.Err_Uebzeit_AnzOk := TRUE;   // Stoerung anzeigen ok, da 2.Zyklus aktiv!
        END_IF;

        #Error := "MW200";   // Schnittstelle nach aussen

        // Stoerung vorhanden:
        #Dyn.Status.Error_aktiv := ("MW200" <> 16#0000);
        IF #Dyn.Status.Error_aktiv THEN
            #hStOk := TRUE;
        END_IF;

        // Antrieb stoppen:
        IF NOT #Dyn.Sk.Auto_akt OR #Dyn.Sk.Hand_akt OR NOT #Dyn.Sk.Bereich_ok THEN
            #Antrieb_Schleuse_stop := TRUE;   // SystemVoraussetzungen fehlen
        END_IF;

        // Stoerungen pro Zyklus neu erzeugen:
        #Dyn.Error.Schleuse_DL := FALSE;
        #Dyn.Error.Undef_Schleuse := FALSE;
        #Dyn.Error.Err_Schritt := FALSE;
        #Dyn.Error.Err_BLKMov := FALSE;
        #Dyn.Error.Err_Raus_Sensor := FALSE;
        #Dyn.Error.Err_Ziel := FALSE;
        #Dyn.Error.Err_Weichen_DL := FALSE;
        #Dyn.Error.Err_Undef_Weiche := FALSE;
        #Dyn.Error.Err_Weiche_belegt := FALSE;
        #Dyn.Error.Barcode_nicht_bekannt := FALSE;

        IF NOT #Dyn.Sk.Err_Uebzeit THEN
            #Dyn.Error.Err_Trolley_nicht_in_Schleuse := FALSE;
            #Dyn.Error.Err_Trolley_raus := FALSE;
            #Dyn.Error.Err_Raus_Sensor := FALSE;
        END_IF;
    END_REGION

    REGION NW7: Statische Schleusen-Parameter holen
        IF #Schleuse.Schleusen_Nr = #Schleusen_Nr THEN   // schon geholt
            #Dyn.Error.Schleuse_DL := FALSE;
            #Dyn.Error.Undef_Schleuse := FALSE;
        ELSE
            #tSuchInt := #Schleusen_Nr;   // Search-Daten (AWL: MW200)
            "DatenOut"(SSDB := #Para.Schleusen_DL_DB,
                       SSDW := 0,
                       PARA := WORD#16#0000,
                       SEARCHDATEN := #tSuchInt,
                       RE_VAL => #tReVal,
                       Position => #tPosition,
                       OUTDATEN := #Schleuse);   // Datensatz direkt in Schleuse-Struktur

            #Dyn.Rueckmld.RET_VAL_DL_Schleuse := #tReVal;

            IF #tReVal.%X7 THEN   // AWL: JBI G_Ok (Datensatz gefunden)
                #Dyn.Error.Schleuse_DL := FALSE;
                #Dyn.Error.Undef_Schleuse := FALSE;
                #Dyn.Rueckmld.RET_VAL_BLKMOV_Schleuse := 0;   // kein BLKMOV mehr noetig
                #Dyn.Error.Err_BLKMov := FALSE;
                #Dyn.Stop_Ursache := 16#A063;

                IF #Schleuse.Schleusen_Typ <> 1274 THEN   // Dieser Aufruf-FB?
                    #Dyn.Error.Undef_Schleuse := TRUE;   // undefinierte Schleuse
                    #Schleuse.Schleusen_Nr := 0;   // Schleuse ungueltig!
                    #Dyn.Stop_Ursache := 16#A064;
                END_IF;
            ELSIF #tReVal.%X0 THEN   // kein Datenlisten-Bereich
                #Dyn.Error.Schleuse_DL := TRUE;   // Schleusendatenbank fehlerhaft
                #Dyn.Stop_Ursache := 16#A061;
            ELSE
                #Dyn.Error.Undef_Schleuse := TRUE;   // undefinierte Schleuse
                #Dyn.Stop_Ursache := 16#A062;
            END_IF;
            GOTO mRET;   // in diesem Zyklus keine Schrittkette
        END_IF;
    END_REGION

    REGION NW8: Einsprung Weiche plus ermitteln
        IF #SAVE.Weichen_DB > 0 THEN   // Einsprung-Zeiger bekannt
            #"Kein_Weichenbereich +1" := FALSE;
        ELSE
            #"Kein_Weichenbereich +1" := (#Schleuse.RausWeichen_Nr = 0);
            IF #"Kein_Weichenbereich +1" THEN
                #Dyn.Error.Err_Undef_Weiche := FALSE;   // keine Weiche projektiert
            ELSE
                #tSuchInt := #Schleuse.RausWeichen_Nr;   // Search-Daten
                "DatenOut"(SSDB := #Para.Weichen_DL_DB,
                           SSDW := 0,
                           PARA := WORD#16#0000,
                           SEARCHDATEN := #tSuchInt,
                           RE_VAL => #tReVal,
                           Position => #tPosition,
                           OUTDATEN := #tOutDaten);

                #Dyn.Rueckmld.RET_VAL_DL_Weiche := #tReVal;

                IF #tReVal.%X7 THEN   // AWL: JBI W_Ok
                    #SAVE.Weichen_DB := WORD_TO_INT(#tOutDaten[2]);   // (AWL: MW224)
                    IF #SAVE.Weichen_DB <= 0 THEN   // sinnvoller Wert?
                        #Dyn.Error.Err_Undef_Weiche := TRUE;   // undefinierte Weiche
                    ELSE
                        #SAVE.Weichen_DW := WORD_TO_INT(#tOutDaten[3]);   // (AWL: MW226)
                    END_IF;
                ELSE
                    #Dyn.Error.Err_Weichen_DL := #tReVal.%X0;   // Weichen-Datenliste fehlerhaft
                    IF NOT #tReVal.%X0 THEN
                        #Dyn.Error.Err_Undef_Weiche := TRUE;   // undefinierte Weiche
                    END_IF;
                END_IF;
            END_IF;
        END_IF;
    END_REGION

    REGION NW9: Trolley in Schleuse entprellen
        IF #B_InSchleuse THEN
            #Dyn.Ohne_B := 0;
        ELSIF #Status2.Trolley_an_Schleuse AND "1s-Impuls" THEN
            #Dyn.Ohne_B := #Dyn.Ohne_B + 1;   // Zeit ohne Sensor +1s
            IF #Dyn.Ohne_B >= 3 THEN   // Nach ca. 3s
                #Status2.Trolley_an_Schleuse := FALSE;
                #Dyn.Ohne_B := 0;
            END_IF;
        END_IF;
    END_REGION

    REGION NW10: Antrieb zusaetzlich starten / stoppen / Schleuse geoeffnet
        IF #ErrQuit OR #"Schleuse_geöffnet" THEN
            #Antrieb_Schleuse_start := TRUE;   // Antrieb starten
            #Antrieb_plus_start := TRUE;   // Kreisel-Antrieb plus starten
        END_IF;

        IF #"Schleuse_geöffnet" THEN
            #Dyn.Status."Trolley_fährt_in_Schleuse" := FALSE;
        END_IF;

        IF #"Schleuse_geöffnet"
           AND (#Dyn.Status.Weiche_geschaltet OR #"Kein_Weichenbereich +1")
           AND #Schleuse.Parameter_allg.Antrieb_Plus_vorhanden THEN
            #Antrieb_plus_vor := TRUE;   // Antrieb plus einschalten
        END_IF;

        IF #"Schleuse_geöffnet"
           AND (#Dyn.Status.Weiche_geschaltet OR #"Kein_Weichenbereich +1")
           AND (#Antrieb_plus_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_Plus_vorhanden) THEN
            #Antrieb_Schleuse_vor := TRUE;   // Antrieb einschalten
        END_IF;

        // Trolley ist in Schleuse und wartet -> Energiesparen nach ca. 3s
        IF (#Status2.Trolley_an_Schleuse AND NOT #"Schleuse_geöffnet"
            AND (NOT #B_VorSchleuse OR #Dyn.Error.Err_Stau_Einfahrt)
            AND NOT #Dyn.Status.Kein_Stop_mehr)
           OR (#"Schleuse_geöffnet"
               AND #Schleuse.Parameter_allg.Antrieb_Plus_vorhanden
               AND NOT #Antrieb_plus_aktiv) THEN
            IF #Dyn.WarteZeit_PS < 3 THEN   // ca. 3s rum?
                IF "1s-Impuls" THEN
                    #Dyn.WarteZeit_PS := #Dyn.WarteZeit_PS + 1;
                END_IF;
            ELSE
                #Antrieb_Schleuse_PS := TRUE;   // Antrieb in Energiesparen schalten
            END_IF;
        ELSE
            #Dyn.WarteZeit_PS := 0;
        END_IF;
    END_REGION

    REGION NW11: Einfahrzeit lernen
        #Einfahrzeit_nicht_bekannt := (#Schleuse.EinfahrZeit = 0) OR #Status2.Einfahrzeit_lernen;

        IF #Einfahrzeit_nicht_bekannt AND #B_VorSchleuse AND #Antrieb_Schleuse_aktiv
           AND NOT #B_InSchleuse AND NOT #"Schleuse_geöffnet" THEN
            #Status2.Einfahrzeit_lernen := TRUE;
        END_IF;

        IF #B_InSchleuse AND NOT #"Schleuse_geöffnet" THEN
            #Status2.Einfahrzeit_lernen := FALSE;
        END_IF;

        IF #Status2.Einfahrzeit_lernen THEN
            #Antrieb_Schleuse_vor := TRUE;   // Antrieb einschalten
        END_IF;

        IF #Status2.Einfahrzeit_lernen AND #Antrieb_Schleuse_aktiv AND "1s-Impuls" THEN
            #Schleuse.EinfahrZeit := #Schleuse.EinfahrZeit + 1;
        END_IF;
    END_REGION

    REGION NW12: PS deaktivieren, da Trolley einfaehrt
        IF NOT #Einfahrzeit_nicht_bekannt THEN   // also bekannt
            IF #B_VorSchleuse THEN   // Neuer Trolley faehrt ein
                #Dyn.Restzeit_bis_Schleuse := #Schleuse.EinfahrZeit;
            END_IF;

            // Restzeit zurueckzaehlen:
            IF #Antrieb_Schleuse_aktiv AND NOT #"Schleuse_geöffnet" AND "1s-Impuls" THEN
                #Dyn.Restzeit_bis_Schleuse := #Dyn.Restzeit_bis_Schleuse - 1;
                IF #Dyn.Restzeit_bis_Schleuse <= 0 THEN
                    #Dyn.Restzeit_bis_Schleuse := 0;   // Minimum uebergeben
                END_IF;
            END_IF;

            // Trolley faehrt noch ein:
            IF (#Dyn.Restzeit_bis_Schleuse > 0) AND NOT #B_InSchleuse AND NOT #"Schleuse_geöffnet" THEN
                #Antrieb_Schleuse_vor := TRUE;   // dadurch wird PS-Modus deaktiviert
            END_IF;
        END_IF;
    END_REGION

    REGION NW13: Ausfahrzeit lernen
        IF #Status2.Ausfahrzeit_lernen AND #Antrieb_Schleuse_aktiv AND "1s-Impuls" THEN
            #Schleuse.AusfahrZeit := #Schleuse.AusfahrZeit + 1;
        END_IF;
    END_REGION

    REGION NW14: Spezial-FCs vorhanden?
        #LeserFC := #Schleuse.Leser_FC;
        #LeserFC_vorhanden := (#LeserFC > 0);

        #ZielFC := #Schleuse.Ziel_FC;
        #ZielFC_vorhanden := (#ZielFC > 0);

        #DraussenFC := #Schleuse.Draussen_FC;
        #DraussenFC_vorhanden := (#DraussenFC > 0);
    END_REGION

    REGION NW15: Error: Stau Bereich-Eingang
        // TrolleyID zum Einfahren gemeldet / Trolley faehrt in Bereich:
        #"TrolleyID_Bereich-1" := (#Dyn.TrolleyID_Eingefahren <> 0);
        IF #"TrolleyID_Bereich-1" OR #B_VorSchleuse THEN
            #Dyn.Status."Trolley_fährt_in_Schleuse" := TRUE;
        END_IF;

        // Antrieb einschalten:
        IF #B_VorSchleuse AND NOT #B_InSchleuse AND NOT #"Schleuse_geöffnet" THEN
            #Antrieb_Schleuse_vor := TRUE;
        END_IF;
        IF #B_VorSchleuse THEN
            #Antrieb_Schleuse_start := TRUE;
        END_IF;

        // Sensor bei Bereichs-Einfahrt bleibt belegt?
        IF NOT #B_VorSchleuse OR #"Schleuse_geöffnet" THEN
            #Dyn.Error.Err_Stau_Einfahrt := FALSE;
            #Dyn.Stau_Zeit := 0;   // StauZeit neu aufziehen
        ELSIF "1s-Impuls"
              AND NOT (#Schleuse.Parameter_allg.Antrieb_vorhanden AND NOT #Antrieb_Schleuse_aktiv)
              AND NOT #Dyn.Error.Err_Stau_Einfahrt THEN
            #Dyn.Stau_Zeit := #Dyn.Stau_Zeit + 1;   // Sensor B_VorSep ist belegt [1s]
            IF #Dyn.Stau_Zeit >= 20 THEN   // Stau-Zeit erreicht?
                #Dyn.Error.Err_Stau_Einfahrt := TRUE;   // Stau bei Einfahrt
                #Dyn.Stau_Zeit := 0;
            END_IF;
        END_IF;
    END_REGION

    REGION NW16: Schleuse Mindestzeit geoeffnet/frei
        IF (#Dyn.Sk.Akt_Schritt < 3) OR (#Dyn.Sk.Akt_Schritt > 6) OR NOT #"Schleuse_geöffnet" THEN
            #Dyn.Status.MindestZeit_ok := FALSE;
            #Dyn.Mindest_Zeit := 0;   // FreiZeit neu aufziehen
        ELSIF NOT ((#Schleuse.Parameter_allg.Antrieb_vorhanden AND NOT #Antrieb_Schleuse_aktiv)
                   OR (#Schleuse.Parameter_allg.Antrieb_Plus_vorhanden AND NOT #Antrieb_plus_aktiv)
                   OR #Dyn.Status.MindestZeit_ok
                   OR NOT "1s-Impuls") THEN
            #Dyn.Mindest_Zeit := #Dyn.Mindest_Zeit + 1;
            IF #Dyn.Mindest_Zeit >= #Schleuse.Mindeszeit_Schleuse_geoeffnet THEN   // Mindest-Zeit erreicht?
                #Dyn.Status.MindestZeit_ok := TRUE;
                #Dyn.Mindest_Zeit := 0;
            END_IF;
        END_IF;
    END_REGION

    REGION NW17: Schleuse: Trolley ist draussen (Sensor/Weiche)
        IF (#Dyn.Sk.Akt_Schritt < 5) OR (#Dyn.Sk.Akt_Schritt > 6) OR NOT #"Schleuse_geöffnet" THEN
            #Status2.Trolley_ist_draussen := FALSE;
            #Status2.Ausfahrzeit_lernen := FALSE;
        ELSE
            // Trolley ist draussen sofort (Rueckmeldung von Sensor/Weiche):
            IF (#B_AusSchleuse AND #Schleuse.Parameter_allg.B_AusSep_vorhanden)
               OR (#Dyn.Status."Trolley ausserhalb Weiche" AND NOT #"Kein_Weichenbereich +1") THEN
                #Status2.Trolley_ist_draussen := TRUE;
            END_IF;
        END_IF;
    END_REGION

    REGION NW18: Einsprung Leser ermitteln
        IF #Dyn.SAVE2.Leser_DB > 0 THEN   // Einsprung-Zeiger bekannt
            #Leser_Zugriff := TRUE;
        ELSIF WORD_TO_INT(#Schleuse.Leser_ID) <> 0 THEN   // Leser angegeben?
            #tSuchInt := WORD_TO_INT(#Schleuse.Leser_ID);   // Search-Daten
            "DatenOut"(SSDB := #Para.Leser_DL_DB,
                       SSDW := 0,
                       PARA := WORD#16#0000,
                       SEARCHDATEN := #tSuchInt,
                       RE_VAL => #tReVal,
                       Position => #tPosition,
                       OUTDATEN := #tOutDaten);

            #Dyn.Rueckmld.RET_VAL_DL_Leser := #tReVal;

            IF #tReVal.%X7 THEN   // AWL: JBI W_OkL
                #Dyn.SAVE2.Leser_DB := WORD_TO_INT(#tOutDaten[1]);   // (AWL: MW222)
                IF #Dyn.SAVE2.Leser_DB <= 0 THEN   // sinnvoller Wert?
                    #Dyn.Error.Err_Leser_nicht_bereit := TRUE;   // undefinierter Leser
                ELSE
                    // (AWL: MD224)
                    #Dyn.SAVE2.Leser_DD := DWORD_TO_DINT(
                        SHL(IN := WORD_TO_DWORD(#tOutDaten[2]), N := 16) OR WORD_TO_DWORD(#tOutDaten[3]));
                    #Dyn.Error.Err_Leser_nicht_bereit := FALSE;
                    #Leser_Zugriff := TRUE;   // Leser-Zugriff vorhanden
                END_IF;
            ELSE
                #Dyn.Error.Err_Leser_nicht_bereit := TRUE;   // Leser-Datenliste fehlerhaft
            END_IF;
        END_IF;
    END_REGION

    REGION NW19: Reservierung bei Weiche plus noch aktiv?
        IF #"Kein_Weichenbereich +1" THEN
            #"Reservierung_Weiche+1_aktiv" := FALSE;
        ELSE
            #tWeichDb := INT_TO_DINT(#SAVE.Weichen_DB);
            #tWeichBase := INT_TO_DINT(#SAVE.Weichen_DW);

            // Schleuse -1 (DBW 78) mit dieser Schleuse belegt?
            #"Reservierung_Weiche+1_aktiv" :=
                (WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tWeichDb,
                                       byteOffset := #tWeichBase + 78)) = #Schleusen_Nr);

            // Mindestzeit nachtriggern (DBX 71.3):
            IF #"Schleuse_geöffnet" AND #B_InSchleuse THEN
                POKE_BOOL(area := 16#84, dbNumber := #tWeichDb,
                          byteOffset := #tWeichBase + 71, bitOffset := 3, value := TRUE);
            END_IF;
        END_IF;
    END_REGION

    REGION NW20: Leser-Einheit managen (Inputs holen)
        IF NOT #Leser_Zugriff OR NOT #Schleuse.Parameter_allg.Leser_eingebaut THEN
            #Dyn.Status.Leser_triggern := FALSE;   // fuer Status-Anzeige
        ELSE
            #tLeserDb := INT_TO_DINT(#Dyn.SAVE2.Leser_DB);
            #tLeserBase := #Dyn.SAVE2.Leser_DD;

            // Offset 10:
            #tByte := PEEK(area := 16#84, dbNumber := #tLeserDb, byteOffset := #tLeserBase + 10);
            #Dyn.Leser."IN".xOutReadingDone := #tByte.%X0;
            #Dyn.Leser."IN".xOutReadyForNewScan := #tByte.%X1;

            // Offset 274:
            #tByte := PEEK(area := 16#84, dbNumber := #tLeserDb, byteOffset := #tLeserBase + 274);
            #Dyn.Leser."IN".xOutCodeGood := #tByte.%X0;
            #Dyn.Leser."IN".xOutCodeBad := #tByte.%X1;

            // Offset 12:
            #Dyn.Leser."IN".nOutBarcode := DWORD_TO_DINT(
                PEEK_DWORD(area := 16#84, dbNumber := #tLeserDb, byteOffset := #tLeserBase + 12));
        END_IF;
    END_REGION

    // NW21 "Schrittkette anhalten": im AWL-Original durch CLR + JU dauerhaft
    // deaktiviert (toter Code) -> entfaellt.

    REGION NW22: Trolley wurde zurueckgezogen
        IF NOT ((#Dyn.Sk.Akt_Schritt < 10) OR (#Dyn.Sk.Akt_Schritt > 12)
                OR #Status2.Trolley_an_Schleuse OR #"Schleuse_geöffnet") THEN
            #Antrieb_Schleuse_start := TRUE;
            #Dyn.Leser."OUT".xInReset := TRUE;

            #Dyn.Sk.Akt_Schritt := 0;   // Neubeginn

            IF #Schleuse.Parameter_allg.Leser_eingebaut THEN   // sonst Daten behalten
                #Dyn.Trolley_ZwischenZiel := 16#0000;
                #Dyn.TrolleyID_Schleuse := 0;
            END_IF;
        END_IF;
    END_REGION

    REGION NW23: Sprungverteiler
        IF #Dyn.Sk.Neu_Schritt <> -1 THEN   // Schritt fuer naechsten Zyklus angegeben?
            #Dyn.Sk.Akt_Schritt := #Dyn.Sk.Neu_Schritt;   // Neuen Schritt uebergeben
            #Dyn.Sk.Neu_Schritt := -1;   // Urzustand
        END_IF;
    END_REGION

mNEU:
    #Warte_neu := FALSE;   // Wartezeit neutriggern
    #Warte_stop := FALSE;   // Wartezeit stoppen

    CASE #Dyn.Sk.Akt_Schritt OF
        0:   // NW25 Schritt 0: Warten auf Trolley
            #Weiter := #B_InSchleuse AND #Dyn.Sk.Auto_akt AND #Dyn.Sk.Bereich_ok;

            // Trolley-Schleusendaten loeschen:
            #Dyn.TrolleyID_Schleuse := 0;
            #Dyn.Trolley_ShopGroup := 0;
            #Dyn.Trolley_EndZiel := 16#0000;
            #Dyn.Weichen_Ziel := 16#0000;
            #Dyn.HMI.TrolleyID_Input := 0;   // HMI-Zeug

            IF #Dyn.Sk.Reset_Akt THEN   // Reset ist aktiv
                #tNeuSchritt := 20;
                GOTO mNeuMig;
            END_IF;

            #Dyn.Error.Err_Leser_nicht_bereit := FALSE;
            #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
            #Dyn.Status.SF_angefordert := FALSE;
            #Status2."TrolleyPos_übergeben" := FALSE;
            #Status2.Telegramm_gesendet := FALSE;

            IF #Weiter THEN   // Weiterschaltbedingung ist erfuellt
                #Dyn.Sk.Err_Uebzeit := FALSE;   // Sofort weiter!
                #tWartezeit := 50;   // Angabe der Wartezeit (*10ms)
                GOTO mPlus;
            END_IF;

            #Dyn.Stop_Ursache := 16#B001;

            IF #Dyn.Status."Trolley_fährt_in_Schleuse" AND NOT #B_InSchleuse THEN
                #Antrieb_Schleuse_vor := TRUE;   // Antrieb einschalten
                // S000A: Ueberwachungszeit starten
                #Ueb_akt := #Dyn.Status."Trolley_fährt_in_Schleuse" AND NOT #B_InSchleuse
                            AND (#Antrieb_Schleuse_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_vorhanden);
                IF #Dyn.Sk.Err_Uebzeit THEN
                    #Dyn.Sk.Err_Uebzeit := FALSE;
                    #Dyn.Error.Err_Trolley_nicht_in_Schleuse := TRUE;
                    #Dyn.Status."Trolley_fährt_in_Schleuse" := FALSE;
                END_IF;
                #tUebzeit := #Schleuse."Übzeit_in_Schleuse";
                GOTO mTueb;
            END_IF;

            #Dyn.Sk.Err_Uebzeit := FALSE;
            #Dyn.Stop_Ursache := 16#B000;
            GOTO mRET;

        1:   // NW26 Schritt 1: Trolley in Schleuse / Leser? / Daten vorhanden?
            IF #B_InSchleuse THEN
                #Status2.Trolley_an_Schleuse := TRUE;
            END_IF;

            IF NOT #Status2.Trolley_an_Schleuse AND #Schleuse.Parameter_allg.Leser_eingebaut THEN
                #tNeuSchritt := 0;   // Weiter mit Schritt 0
                GOTO mNeuMig;
            END_IF;

            // Zwischen-Ziel von Bereich -1 vorhanden?
            IF (#Dyn.ZwischenZiel_eingefahren <> 16#0000)
               AND NOT #Schleuse.Parameter_allg.Leser_eingebaut THEN
                // Daten von Bereich -1 uebergeben:
                #Dyn.Trolley_ZwischenZiel := #Dyn.ZwischenZiel_eingefahren;
                #Dyn.ZwischenZiel_eingefahren := 16#0000;
                #Dyn.TrolleyID_Schleuse := #Dyn.TrolleyID_Eingefahren;
                #Dyn.TrolleyID_Eingefahren := 0;
                #tNeuSchritt := 12;   // Weiter mit Schritt 12
                GOTO mNeuMig;
            END_IF;

            // X001:
            #Dyn.Trolley_ZwischenZiel := 16#0000;

            IF #Schleuse.Parameter_allg.Leser_eingebaut THEN   // Leser vorhanden
                #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
                #tNeuSchritt := 9;   // Weiter mit Schritt 9 (Leser bereit)
                GOTO mNeuMig;
            END_IF;

            // Trolleydaten vorhanden? (Uebergabe von Bereich -1)
            #Dyn.TrolleyID_Schleuse := #Dyn.TrolleyID_Eingefahren;
            IF #Dyn.TrolleyID_Eingefahren <> 0 THEN
                #tNeuSchritt := 11;   // Weiter mit Schritt 11 (Trolleydaten lesen)
                GOTO mNeuMig;
            END_IF;

            IF #ZielFC_vorhanden THEN
                #tNeuSchritt := 12;   // Weiter mit Schritt 12 (Ziel-FC)
                GOTO mNeuMig;
            END_IF;

            #tNeuSchritt := 14;   // Weiter mit Schritt 14 (Weichenbereich +1 vorhanden)
            GOTO mNeuMig;

        2:   // NW27 Schritt 2: Ausfahren ok?
            IF NOT #Status2.Trolley_an_Schleuse AND #Schleuse.Parameter_allg.Leser_eingebaut THEN
                #tNeuSchritt := 0;   // Weiter mit Schritt 0
                GOTO mNeuMig;
            END_IF;

            // Anlage aus oder Stoerung:
            #Dyn.Stop_Ursache := 16#B021;
            IF NOT #Dyn.Sk.Auto_akt OR NOT #Dyn.Sk.Bereich_ok THEN
                GOTO mRET;
            END_IF;

            // Starten erlaubt?
            #Dyn.Stop_Ursache := 16#B022;
            IF NOT #Start_Ok THEN
                GOTO mRET;
            END_IF;

            // Stau im Bereich +1:
            #Dyn.Stop_Ursache := 16#B023;
            #Dyn.Status."Trolley_wartet_Bereich+1" := TRUE;
            IF #B_Stau_raus THEN   // Sensor Stau im Bereich rausfahren
                GOTO mRET;
            END_IF;

            // B_Aus_Schleuse aktiv:
            #Dyn.Stop_Ursache := 16#B024;
            #Dyn.Error.Err_Raus_Sensor := #Schleuse.Parameter_allg.B_AusSep_vorhanden AND #B_AusSchleuse;
            IF #Dyn.Error.Err_Raus_Sensor THEN
                GOTO mRET;
            END_IF;

            // Restzeit zum Ausfahren ausreichend?
            #Dyn.Stop_Ursache := 16#B025;
            IF ((#RestZeit_Bereich_plus < #Schleuse.AusfahrZeit) OR (#Schleuse.AusfahrZeit = 0))
               AND (#RestZeit_Bereich_plus <> 999) THEN
                RETURN;   // AWL: BEC (Baustein sofort beenden, NW50 entfaellt)
            END_IF;

            // Antriebe aktivieren / aktiv?
            IF #Schleuse.Parameter_allg.Antrieb_Plus_vorhanden THEN
                #Antrieb_plus_vor := TRUE;
            END_IF;
            IF #Antrieb_plus_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_Plus_vorhanden THEN
                #Antrieb_Schleuse_vor := TRUE;
            END_IF;

            #Dyn.Stop_Ursache := 16#B026;
            IF NOT #Antrieb_plus_aktiv AND #Schleuse.Parameter_allg.Antrieb_Plus_vorhanden THEN
                GOTO mRET;
            END_IF;

            #Dyn.Status.Kein_Stop_mehr := NOT #Antrieb_Schleuse_aktiv
                                          AND #Schleuse.Parameter_allg.Antrieb_vorhanden;
            IF #Dyn.Status.Kein_Stop_mehr THEN
                GOTO mRET;
            END_IF;

            #Status2."Warten_3.V" := FALSE;

            IF NOT #Schleuse.Parameter_allg."2.V_bei_Ausfahrt_checken" THEN
                #tNeuSchritt := 5;   // Nur 1 langes Signal!
            ELSE
                #tNeuSchritt := 3;   // 2.V bei Ausfahrt checken
            END_IF;
            GOTO mNeuMig;

        3:   // NW28 Schritt 3: Trolley faehrt aus / 1.V ausgefahren?
            #Weiter := NOT #B_InSchleuse;

            #Y_Schleuse_Auf := TRUE;   // Schleuse oeffnen
            #Dyn.Status."Trolley_fährt_aus" := TRUE;

            IF #Weiter THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;   // Sofort weiter!
                #Dyn.Error.Err_Trolley_raus := FALSE;
                #tWartezeit := 30;   // Angabe der Wartezeit (*10ms)
                GOTO mPlus;
            END_IF;

            IF NOT #"Kein_Weichenbereich +1" AND NOT #"Reservierung_Weiche+1_aktiv" THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;   // Weiche nicht mehr angemeldet
                #tNeuSchritt := 0;   // Weiter mit Schritt 0
                GOTO mNeuMig;
            END_IF;

            IF #Dyn.Sk.Err_Uebzeit THEN
                #Dyn.Error.Err_Trolley_raus := TRUE;   // Trolley faehrt nicht aus
            END_IF;

            #Dyn.Stop_Ursache := 16#B031;
            #Ueb_akt := #Antrieb_Schleuse_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_vorhanden;
            #tUebzeit := #Schleuse."Übzeit_aus_Schleuse";
            GOTO mTueb;

        4:   // NW29 Schritt 4: Trolley faehrt aus / Warten auf 2.V
            #Weiter := #B_InSchleuse OR #Dyn.Status."Trolley ausserhalb Weiche";

            // Bei Raus-Sensor nicht zaehlen:
            IF #Schleuse.Parameter_allg.B_AusSep_vorhanden AND #B_AusSchleuse THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;
                #Dyn.Error.Err_Trolley_raus := FALSE;
                #tNeuSchritt := 5;   // Zaehlen ueberspringen
                GOTO mNeuMig;
            END_IF;

            #Y_Schleuse_Auf := TRUE;   // Schleuse oeffnen
            #Dyn.Status."Trolley_fährt_aus" := TRUE;

            IF #Weiter THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;
                #Dyn.Error.Err_Trolley_raus := FALSE;
                #tWartezeit := 0;
                GOTO mPlus;
            END_IF;

            IF NOT #"Kein_Weichenbereich +1" AND NOT #"Reservierung_Weiche+1_aktiv" THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;
                #tNeuSchritt := 0;
                GOTO mNeuMig;
            END_IF;

            IF #Dyn.Sk.Err_Uebzeit THEN
                #Dyn.Error.Err_Trolley_raus := TRUE;
            END_IF;

            #Dyn.Stop_Ursache := 16#B041;
            #Ueb_akt := #Antrieb_Schleuse_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_vorhanden;
            #tUebzeit := #Schleuse."Übzeit_aus_Schleuse";
            GOTO mTueb;

        5:   // NW30 Schritt 5: Trolley faehrt aus?
            #Weiter := NOT #B_InSchleuse
                       AND (#Antrieb_Schleuse_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_vorhanden)
                       AND (#Antrieb_plus_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_Plus_vorhanden);

            // Ausfahrzeit lernen:
            IF NOT #Schleuse.Parameter_allg.B_AusSep_vorhanden THEN
                #Schleuse.AusfahrZeit := #Schleuse."Übzeit_aus_Schleuse";   // ohne Sensor: Ueb.Zeit uebergeben
            END_IF;
            IF (#Schleuse.AusfahrZeit = 0) AND #Schleuse.Parameter_allg.B_AusSep_vorhanden THEN
                #Status2.Ausfahrzeit_lernen := TRUE;
            END_IF;

            #Y_Schleuse_Auf := TRUE;   // Schleuse oeffnen
            #Dyn.Status."Trolley_fährt_aus" := TRUE;

            IF #B_InSchleuse THEN   // B in Schleuse aktiv
                #Dyn.Sk.Tok_Zelle := 0;
            END_IF;

            // Trolley ist draussen mit Verzoegerung:
            IF #Weiter THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;
                #Dyn.Error.Err_Trolley_raus := FALSE;
                #tWartezeit := 300;
                GOTO mPlus;
            END_IF;

            // Trolley faehrt nicht raus!
            IF #Dyn.Sk.Err_Uebzeit THEN
                #Dyn.Error.Err_Trolley_raus := TRUE;
            END_IF;

            #Dyn.Stop_Ursache := 16#B051;
            #Ueb_akt := #Antrieb_Schleuse_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_vorhanden;
            #tUebzeit := #Schleuse."Übzeit_aus_Schleuse";
            GOTO mTueb;

        6:   // NW31 Schritt 6: Schleuse Mindestzeit geoeffnet / Trolley draussen?
            #Weiter := #Dyn.Status.MindestZeit_ok AND NOT #B_InSchleuse
                       AND ((NOT #Schleuse.Parameter_allg.B_AusSep_vorhanden AND #"Kein_Weichenbereich +1")
                            OR #Status2.Trolley_ist_draussen);
            IF #Weiter THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;   // Sofort weiter!
                #Dyn.Error.Err_Trolley_raus := FALSE;
            END_IF;

            #Y_Schleuse_Auf := TRUE;   // Schleuse oeffnen
            #Dyn.Status."Trolley_fährt_aus" := TRUE;

            // Erneutes Signal von B in Schleuse?
            IF #B_InSchleuse THEN
                #Dyn.Sk.Neu_Schritt := 5;
                #Dyn.Mindest_Zeit := 0;   // Mindest-Zeit neu starten
                #Dyn.Status.MindestZeit_ok := FALSE;
                GOTO mRET;
            END_IF;

            IF NOT #Weiter THEN   // X060
                #Dyn.Stop_Ursache := 16#B061;

                // Trolley faehrt nicht raus!
                IF #Dyn.Sk.Err_Uebzeit THEN
                    #Dyn.Error.Err_Trolley_raus := TRUE;
                END_IF;
                IF #Dyn.Error.Err_Trolley_raus AND NOT #Status2.Trolley_ist_draussen
                   AND #Schleuse.Parameter_allg.B_AusSep_vorhanden THEN
                    #Dyn.Error.Err_Trolley_raus := FALSE;
                    #Dyn.Error.Err_Raus_Sensor := TRUE;   // Raus-Sensor wird nicht erreicht!
                END_IF;

                #Ueb_akt := #Antrieb_Schleuse_aktiv OR NOT #Schleuse.Parameter_allg.Antrieb_vorhanden;
                #tUebzeit := #Schleuse."Übzeit_aus_Schleuse";
                GOTO mTueb;
            END_IF;

            // X006: Trolley hat Schleuse verlassen
            #Dyn.Status.Trolley_draussen := TRUE;   // 1 Zyklus
            #Dyn.Status."Trolley_fährt_in_Schleuse" := FALSE;
            #Status2.Telegramm_gesendet := FALSE;

            #Dyn.Vereinzelungstakte := #Dyn.Vereinzelungstakte + 1;

            // Draussen-FC?
            IF #DraussenFC_vorhanden THEN
                "MW190" := INT_TO_WORD(#Schleusen_Nr);   // Schleusen-Nr uebergeben
                "MD182" := DINT_TO_DWORD(#Dyn.Trolley_ShopGroup);   // Shop-Gruppe
                "MW198" := #Dyn.Weichen_Ziel;   // Weichenziel

                CASE #DraussenFC OF   // AWL: UC FC[#DraussenFC]
                    1182: "Arbeitsplatz_Draussen_FC"();
                    ELSE ;   // unbekannter Draussen-FC
                END_CASE;
            END_IF;

            #tNeuSchritt := 7;   // Weiter mit Schritt 7
            GOTO mNeuMig;

        7:   // NW32 Schritt 7: Trolley ausserhalb der Weiche
            #Weiter := (NOT #"Kein_Weichenbereich +1" AND NOT #"Reservierung_Weiche+1_aktiv")
                       OR #Dyn.Status."Trolley ausserhalb Weiche"
                       OR #"Kein_Weichenbereich +1";

            #Y_Schleuse_Auf := TRUE;   // Schleuse oeffnen
            #Dyn.Status."Trolley_fährt_aus" := TRUE;
            IF #Dyn.Status.Weiche_geschaltet OR #"Kein_Weichenbereich +1" THEN
                #Antrieb_Schleuse_vor := TRUE;
                #Antrieb_plus_vor := TRUE;
            END_IF;

            IF NOT #Weiter THEN
                #Dyn.Stop_Ursache := 16#B071;
                GOTO mRET;
            END_IF;

            // X007: Trolley ist ausserhalb der Weiche?
            IF #"Kein_Weichenbereich +1" THEN
                GOTO mEndeMig;
            END_IF;

            // Weichen-Rueckmeldungen loeschen:
            #Dyn.Status.Weiche_geschaltet := FALSE;
            #Dyn.Status."Trolley ausserhalb Weiche" := FALSE;

            // Weichen-Reservierung loeschen:
            #tWeichDb := INT_TO_DINT(#SAVE.Weichen_DB);
            #tWeichBase := INT_TO_DINT(#SAVE.Weichen_DW);
            IF WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tWeichDb,
                                     byteOffset := #tWeichBase + 78)) <> #Schleusen_Nr THEN
                GOTO mEndeMig;   // nicht diese Schleuse
            END_IF;

            POKE(area := 16#84, dbNumber := #tWeichDb, byteOffset := #tWeichBase + 78,
                 value := WORD#16#0000);   // Schleuse -1 (DBW 78) loeschen
            #SAVE."Weiche+1" := 0;   // Hier wurde reserviert

            #tWartezeit := 0;
            GOTO mPlus;   // Naechsten Schritt starten (Schritt 8)

        8:   // NW33 Schritt 8: Weichen-Rueckmeldungen = 0?
            IF NOT #Dyn.Status.Weiche_geschaltet AND NOT #Dyn.Status."Trolley ausserhalb Weiche" THEN
                GOTO mEndeMig;
            END_IF;
            #Dyn.Stop_Ursache := 16#B080;
            GOTO mRET;

        9:   // NW34 Schritt 9: Lese-Einheit bereit?
            #Weiter := (#Dyn.Leser."IN".nOutBarcode = 0)
                       AND #Dyn.Leser."IN".xOutReadyForNewScan
                       AND NOT #Dyn.Leser."IN".xOutReadingDone
                       AND NOT #Dyn.Leser."IN".xOutCodeGood
                       AND NOT #Dyn.Leser."IN".xOutCodeBad;
            IF #Weiter THEN
                #Dyn.Error.Err_Leser_nicht_bereit := FALSE;
            END_IF;

            IF NOT #Status2.Trolley_an_Schleuse AND #Schleuse.Parameter_allg.Leser_eingebaut THEN
                #Dyn.Error.Err_Leser_nicht_bereit := FALSE;
                #tNeuSchritt := 0;   // Weiter mit Schritt 0
                GOTO mNeuMig;
            END_IF;

            #Dyn.Leser."OUT".xInStartReading := #Weiter;   // Leser triggern
            IF #Weiter THEN
                #tWartezeit := 0;
                GOTO mPlus;
            END_IF;

            // Leser nicht bereit:
            IF #Dyn.Leser."IN".xOutReadingDone OR #Dyn.Leser."IN".xOutCodeGood
               OR #Dyn.Leser."IN".xOutCodeBad THEN
                #Dyn.Leser."OUT".xInHandshakeDone := TRUE;   // Falls altes Handshake noch gewollt
            END_IF;

            IF #Dyn.Sk.Err_Uebzeit THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;   // Sofort weiter!
                #Dyn.Error.Err_Leser_nicht_bereit := TRUE;
                #Dyn.Leser."OUT".xInErrQuit := TRUE;   // Vielleicht hilft es
            END_IF;

            IF #Dyn.Error.Err_Leser_nicht_bereit AND #ErrQuit THEN
                #Dyn.Leser."OUT".xInReset := TRUE;   // Mit Quittierung zusaetzlich reset!
            END_IF;

            // HMI-TrolleyID bei Leser nicht bereit:
            IF (#Dyn.HMI.TrolleyID_Input <> 0) AND #Dyn.Error.Err_Leser_nicht_bereit THEN
                #tWartezeit := 0;
                GOTO mPlus;
            END_IF;

            #Ueb_akt := TRUE;
            #tUebzeit := 5;   // Angabe der Ueberwachungszeit (*1s)
            GOTO mTueb;

        10:   // NW35 Schritt 10: Warten auf Leseeinheit
            #Weiter := #Dyn.Leser."IN".xOutReadingDone
                       AND #Dyn.Leser."IN".xOutCodeGood
                       AND NOT #Dyn.Leser."IN".xOutCodeBad;
            IF #Weiter THEN
                #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
            END_IF;

            IF NOT #Status2.Trolley_an_Schleuse AND #Schleuse.Parameter_allg.Leser_eingebaut THEN
                #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
                #tNeuSchritt := 0;   // Weiter mit Schritt 0
                GOTO mNeuMig;
            END_IF;

            #Dyn.Leser."OUT".xInStartReading := FALSE;   // Leseeinheit triggern

            IF #Weiter THEN
                // X010: Leserdaten uebergeben
                #Dyn.Leser."OUT".xInHandshakeDone := TRUE;
                #Dyn.Sk.Err_Uebzeit := FALSE;   // Sofort weiter!

                #Dyn.TrolleyID_Schleuse := #Dyn.Leser."IN".nOutBarcode;
                IF (#Dyn.Leser."IN".nOutBarcode = 0) OR (#Dyn.Leser."IN".nOutBarcode = -1) THEN
                    #Dyn.Error.Err_Kein_LeseErgebnis := TRUE;   // Unbekannt / Fehl-Lesung
                    #tNeuSchritt := 9;   // Weiter mit Schritt 9 (Leser bereit)
                    GOTO mNeuMig;
                END_IF;
                #tNeuSchritt := 11;   // Weiter mit Schritt 11
                GOTO mNeuMig;
            END_IF;

            #Dyn.Status.Trolley_wartet_Lesung := TRUE;

            IF NOT #Dyn.Error.Err_Leser_nicht_bereit THEN   // von Schritt 9
                IF #Dyn.Sk.Err_Uebzeit OR #Dyn.Leser."IN".xOutCodeBad THEN
                    #Dyn.Leser."OUT".xInHandshakeDone := TRUE;
                    // L2: Erst bei 3.Fehllesung Error!
                    IF NOT #Status2."2.Lesung_aktiv" THEN
                        #Status2."2.Lesung_aktiv" := TRUE;
                        #Dyn.Sk.Err_Uebzeit := FALSE;   // Sofort weiter!
                        #Dyn.Sk.Neu_Schritt := 9;
                        GOTO mRET;
                    END_IF;
                    // L3:
                    IF NOT #Status2."3.Lesung_aktiv" THEN
                        #Status2."3.Lesung_aktiv" := TRUE;
                        #Dyn.Sk.Err_Uebzeit := FALSE;   // Sofort weiter!
                        #Dyn.Sk.Neu_Schritt := 9;
                        GOTO mRET;
                    END_IF;
                    // LX: Nach 3.Fehl-Lesung erst Leserfehler!
                    #Dyn.Sk.Err_Uebzeit := FALSE;
                    #Dyn.Error.Err_Kein_LeseErgebnis := TRUE;
                ELSE
                    #Dyn.Stop_Ursache := 16#B101;
                    #Ueb_akt := TRUE;
                    #tUebzeit := 10;   // Angabe der Ueberwachungszeit (*1s)
                    GOTO mTueb;
                END_IF;
            ELSE
                // LX (direkt von Schritt-9-Fehler):
                #Dyn.Sk.Err_Uebzeit := FALSE;
                #Dyn.Error.Err_Kein_LeseErgebnis := TRUE;
            END_IF;

            // NoLesung: HMI-Eingabe?
            #Dyn.TrolleyID_Schleuse := #Dyn.HMI.TrolleyID_Input;
            IF #Dyn.HMI.TrolleyID_Input <> 0 THEN   // ACHTUNG: AWL verglich mit undefiniertem Akku!
                #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
                #Dyn.Error.Err_Leser_nicht_bereit := FALSE;   // von Schritt 9
                #tWartezeit := 0;
                GOTO mPlus;
            END_IF;

            // No_HMIa: Leser-FC aufrufen:
            IF #LeserFC_vorhanden THEN
                "MW190" := INT_TO_WORD(#Schleusen_Nr);   // Schleusen-Nr uebergeben
                "MW196" := #Dyn.Trolley_ZwischenZiel;
                "M180.0" := #Dyn.Error.Err_Kein_LeseErgebnis;
                "M180.1" := FALSE;

                CASE #LeserFC OF   // AWL: UC FC[#LeserFC] (Beeinflussung der Fehllesung)
                    1180: "Arbeitsplatz_Leser_FC"();
                    ELSE ;   // unbekannter Leser-FC
                END_CASE;

                #Dyn.Stop_Ursache := 16#B102;

                #Dyn.Trolley_ZwischenZiel := "MW196";   // Zwischenziel uebergeben
                IF "MW196" <> 16#0000 THEN
                    #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
                    #tNeuSchritt := 12;   // Weiter mit Schritt 12
                    GOTO mNeuMig;
                END_IF;
            END_IF;

            // NoLFC:
            #Dyn.Sk.Neu_Schritt := 9;
            GOTO mRET;

        11:   // NW36 Schritt 11: Trolleydaten aus DL lesen
            #Dyn.TrolleyID_Eingefahren := 0;   // Eintrag von Weiche -1

            #tSuchID := #Dyn.TrolleyID_Schleuse;   // Search-Daten (AWL: MD220)
            "DatenOut"(SSDB := #Schleuse.Gesamt_Trolley_DL,
                       SSDW := 0,
                       PARA := WORD#16#0000,
                       SEARCHDATEN := #tSuchID,
                       RE_VAL => #tReVal,
                       Position => #tPosition,
                       OUTDATEN := "TrolleyDL_OutDaten".OUTDaten);   // Eintrag lesen (AWL: DB1011.DBX0.0)

            #Dyn.Stop_Ursache := 16#B111;

            #Dyn.Error.Barcode_nicht_bekannt := NOT #tReVal.%X7;
            IF NOT #Dyn.Error.Barcode_nicht_bekannt THEN
                // Relevante Trolleydaten auslesen:
                #Dyn.Trolley_EndZiel := "TrolleyDL_OutDaten".OUTDaten.EndZiel;

                IF #Dyn.Trolley_ZwischenZiel = 16#0000 THEN   // ZwischenZiel nicht ueberschreiben
                    #Dyn.Trolley_ZwischenZiel := "TrolleyDL_OutDaten".OUTDaten.ZwischenZiel;
                END_IF;

                #Dyn.Trolley_ShopGroup := "TrolleyDL_OutDaten".OUTDaten.ShopGroup;

                #tWartezeit := 0;
                GOTO mPlus;   // Naechsten Schritt starten
            END_IF;

            // UnbekanntBC / Nix_HMI: Leser-FC aufrufen:
            IF #LeserFC_vorhanden THEN
                "MW190" := INT_TO_WORD(#Schleusen_Nr);
                "MD192" := DINT_TO_DWORD(#Dyn.TrolleyID_Schleuse);
                "MW196" := #Dyn.Trolley_ZwischenZiel;
                "M180.1" := #Dyn.Error.Barcode_nicht_bekannt;
                "M180.0" := FALSE;

                CASE #LeserFC OF   // AWL: UC FC[#LeserFC] (Weiterfahrt bei nicht bekanntem Trolley)
                    1180: "Arbeitsplatz_Leser_FC"();
                    ELSE ;
                END_CASE;

                #Dyn.Trolley_ZwischenZiel := "MW196";   // ZwischenZiel uebergeben
                IF "MW196" <> 16#0000 THEN
                    #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
                    #tNeuSchritt := 12;   // Weiter mit Schritt 12
                    GOTO mNeuMig;
                END_IF;
            END_IF;

            // Nix_LFC:
            GOTO mRET;

        12:   // NW37 Schritt 12: Ziel-FC
            IF NOT #Status2.Trolley_an_Schleuse AND #Schleuse.Parameter_allg.Leser_eingebaut
               AND NOT #"Schleuse_geöffnet" THEN
                #tNeuSchritt := 0;   // Weiter mit Schritt 0
                GOTO mNeuMig;
            END_IF;

            #Dyn.Status."Trolley_wartet_auf_Ziel-FC" := TRUE;

            IF NOT #ZielFC_vorhanden THEN
                #Dyn.Status."Trolley_wartet_auf_Ziel-FC" := FALSE;
                #tNeuSchritt := 13;   // Weiter mit Schritt 13
                GOTO mNeuMig;
            END_IF;

            "M180.6" := FALSE;   // Rueckmeldungen erst mal resetten
            "MW190" := INT_TO_WORD(#Schleusen_Nr);   // Schleusen-Nr uebergeben
            "MD182" := DINT_TO_DWORD(#Dyn.Trolley_ShopGroup);   // Shop-Gruppe
            "MW194" := #Dyn.Trolley_EndZiel;   // End-Ziel
            "MW196" := #Dyn.Trolley_ZwischenZiel;
            "MW198" := 16#0000;   // Weichenziel erst mal nullen

            CASE #ZielFC OF   // AWL: UC FC[#ZielFC] (Beeinflussung der Weiterfahrt)
                181:  "HWtoAMRZielFC"();
                1181: "Arbeitsplatz_Ziel_FC"();
                2181: "HWtoOB_Ziel_FC"();
                ELSE ;   // unbekannter Ziel-FC
            END_CASE;

            #Dyn.Stop_Ursache := 16#B122;

            // Spezial-Fall bei Uebergabe an Sorter / Turm (M180.6 wird nie im Ziel-FC gesetzt!)
            // aber anderer Partner hat Schleuse extern geoeffnet:
            IF #"Schleuse_geöffnet" AND NOT "M180.6" AND NOT #Status2."TrolleyPos_übergeben" THEN
                #Status2."TrolleyPos_übergeben" := TRUE;   // Nur einmalig

                #Dyn.Vereinzelungstakte := #Dyn.Vereinzelungstakte + 1;

                "Trolley_DL_Neue_Pos"(Gesamt_Trolley_DL := #Schleuse.Gesamt_Trolley_DL,
                                      TrolleyID := #Dyn.TrolleyID_Schleuse,
                                      Position := #Schleuse.Leser_ID,
                                      ShopGroup := DINT#0,
                                      Err => "M199.0");

                // Draussen-FC?
                IF NOT #DraussenFC_vorhanden THEN
                    GOTO mRET;
                END_IF;

                "MW190" := INT_TO_WORD(#Schleusen_Nr);
                CASE #DraussenFC OF   // AWL: UC FC[#DraussenFC] (z.B. Daten anpassen)
                    1182: "Arbeitsplatz_Draussen_FC"();
                    ELSE ;
                END_CASE;
                GOTO mRET;
            END_IF;

            // Normal:
            IF "M180.6" THEN   // Weiterfahrt von Ziel-FC erlaubt
                // X012:
                #Dyn.Trolley_ZwischenZiel := "MW196";   // zurueckschreiben, falls veraendert
                #Dyn.Weichen_Ziel := "MW198";   // Weichenziel uebergeben
                #tNeuSchritt := 13;   // Weiter mit Schritt 13
                GOTO mNeuMig;
            END_IF;

            IF NOT #Schleuse.Parameter_allg.Leser_eingebaut THEN
                GOTO mRET;
            END_IF;

            #Dyn.Sk.Neu_Schritt := 11;
            GOTO mRET;

        13:   // NW38 Schritt 13: Schleusen-Position zurueckschreiben
            #Status2."Weichen-Schritt" := TRUE;

            // Neue Position an Gesamt-Trolley-DL uebergeben:
            "Trolley_DL_Neue_Pos"(Gesamt_Trolley_DL := #Schleuse.Gesamt_Trolley_DL,
                                  TrolleyID := #Dyn.TrolleyID_Schleuse,
                                  Position := #Schleuse.Leser_ID,
                                  ShopGroup := DINT#0,
                                  Err => "M199.0");

            #tWartezeit := 0;
            GOTO mPlus;   // Naechsten Schritt starten

        14:   // NW39 Schritt 14: Weichenbereich +1 vorhanden
            IF #"Kein_Weichenbereich +1" THEN
                #tNeuSchritt := 2;   // Weiter mit Schritt 2 (Ausfahren ok?)
                GOTO mNeuMig;
            END_IF;

            #Status2."Weichen-Schritt" := TRUE;
            #Dyn.Status.Weiche_geschaltet := FALSE;
            #Dyn.Status."Trolley ausserhalb Weiche" := FALSE;

            #tWartezeit := 0;
            GOTO mPlus;

        15:   // NW40 Schritt 15: Weichenbereich raus bereit?
            #Status2."Weichen-Schritt" := TRUE;

            IF #Dyn.Weichen_Ziel = 16#0000 THEN   // Weichenziel bekannt?
                #Dyn.Sk.Neu_Schritt := 12;   // Ziel-FC
                #Dyn.Stop_Ursache := 16#B154;
                GOTO mRET;
            END_IF;

            // X15A:
            #Dyn.Status."Trolley_wartet_auf_Weiche+1" := TRUE;
            #Dyn.Stop_Ursache := 16#B155;

            IF #SAVE.Weichen_DB <= 0 THEN   // Einsprung-Zeiger bekannt?
                #Dyn.Stop_Ursache := 16#B150;
                GOTO mRET;
            END_IF;

            // S015A: Weichendaten auslesen
            #tWeichDb := INT_TO_DINT(#SAVE.Weichen_DB);
            #tWeichBase := INT_TO_DINT(#SAVE.Weichen_DW);

            #Dyn.Stop_Ursache := 16#B152;

            IF NOT (NOT PEEK_BOOL(area := 16#84, dbNumber := #tWeichDb,
                                  byteOffset := #tWeichBase + 70, bitOffset := 0)   // Weichenbereich frei? (DBX70.0)
                    OR #Dyn.Status.Weiche_geschaltet
                    OR #Dyn.Status."Trolley ausserhalb Weiche") THEN
                #tNeuSchritt := 16;   // Weiter mit Schritt 16 (Daten an Weiche uebergeben)
                GOTO mNeuMig;
            END_IF;

            // S015B: Reservierung schon erfolgt?
            #Dyn.Status.Weiche_geschaltet := FALSE;
            #Dyn.Status."Trolley ausserhalb Weiche" := FALSE;

            IF WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tWeichDb,
                                     byteOffset := #tWeichBase + 78)) = #Schleusen_Nr THEN
                // Reservierung loeschen:
                POKE(area := 16#84, dbNumber := #tWeichDb, byteOffset := #tWeichBase + 78,
                     value := WORD#16#0000);   // Schleuse -1 (DBW 78)
            END_IF;
            GOTO mRET;

        16:   // NW41 Schritt 16: Daten an Rausweiche uebergeben
            #Status2."Weichen-Schritt" := TRUE;

            #tWeichDb := INT_TO_DINT(#SAVE.Weichen_DB);
            #tWeichBase := INT_TO_DINT(#SAVE.Weichen_DW);

            // Trolley-ID (DBD 72):
            POKE(area := 16#84, dbNumber := #tWeichDb, byteOffset := #tWeichBase + 72,
                 value := DINT_TO_DWORD(#Dyn.TrolleyID_Schleuse));

            // Weichenziel (DBW 76):
            POKE(area := 16#84, dbNumber := #tWeichDb, byteOffset := #tWeichBase + 76,
                 value := #Dyn.Weichen_Ziel);

            // Zwischenziel (DBW 80):
            POKE(area := 16#84, dbNumber := #tWeichDb, byteOffset := #tWeichBase + 80,
                 value := #Dyn.Trolley_ZwischenZiel);

            // Schleuse -1 (DBW 78):
            POKE(area := 16#84, dbNumber := #tWeichDb, byteOffset := #tWeichBase + 78,
                 value := INT_TO_WORD(#Schleusen_Nr));

            #"Reservierung_Weiche+1_aktiv" := TRUE;   // (da erst im naechsten Zyklus gelesen)

            #SAVE."Weiche+1" := #Schleuse.RausWeichen_Nr;   // Hier wurde reserviert

            #tNeuSchritt := 17;   // Weiter mit Schritt 17 (Hat Weiche geschaltet?)
            GOTO mNeuMig;

        17:   // NW42 Schritt 17: Hat Rausweiche geschaltet?
            #Status2."Weichen-Schritt" := TRUE;

            // Weiche +1 noch mit dieser Schleuse aktiv?
            IF NOT #"Reservierung_Weiche+1_aktiv" THEN
                #tNeuSchritt := 14;   // Weiter mit Schritt 14
                GOTO mNeuMig;
            END_IF;

            #Dyn.Status."Trolley_wartet_auf_Weiche+1" := TRUE;
            #Dyn.Stop_Ursache := 16#B170;

            IF #Dyn.Status."Trolley ausserhalb Weiche" THEN   // Rueckmeldung von Weiche +1
                GOTO mRET;
            END_IF;

            IF #Dyn.Status.Weiche_geschaltet THEN   // Rueckmeldung von Weiche +1
                #tNeuSchritt := 2;   // Weiter mit Schritt 2 (Ausfahren ok?)
                GOTO mNeuMig;
            END_IF;

            #Dyn.Stop_Ursache := 16#B171;
            GOTO mRET;

        20:   // NW43 Schritt 20: Schleuse resetten (Wartezeit)
            #Weiter := #B_InSchleuse;

            // ELS in Grundstellung fahren:
            #Antrieb_Schleuse_vor := TRUE;
            #Antrieb_Schleuse_start := TRUE;

            IF #Weiter OR #Dyn.Sk.Err_Uebzeit THEN
                #Dyn.Sk.Err_Uebzeit := FALSE;   // Sofort weiter!
                #tNeuSchritt := 21;   // Weiter mit Schritt 21
                GOTO mNeuMig;
            END_IF;

            #Dyn.Stop_Ursache := 16#B201;
            #Ueb_akt := TRUE;
            #tUebzeit := 60;   // AWL: L Wartezeit_bis_leer; L 60 -> 60 wirksam
            GOTO mTueb;

        21:   // NW44 Schritt 21: Reset ist zu Ende
            #Dyn.Sk.Reset_Akt := FALSE;   // Reset ist aktiv
            #Status2."Warten_3.V" := FALSE;
            #Status2."TrolleyPos_übergeben" := FALSE;
            #Dyn.Error.Err_Trolley_nicht_in_Schleuse := FALSE;
            #Dyn.Error.Err_Uebzeit := FALSE;
            #Dyn.Error.Err_Stau_Einfahrt := FALSE;
            #Dyn.Error.Err_Trolley_raus := FALSE;
            #Dyn.Status."Trolley_fährt_in_Schleuse" := FALSE;

            #SAVE.Weichen_DB := 0;
            #SAVE.Weichen_DW := 0;
            #SAVE."Weiche+1" := 0;   // Dadurch wird in Weiche Reservierung geloescht
            #Schleuse.Schleusen_Nr := 0;   // Damit stat. Daten neu geholt werden!

            #Dyn.Status.Trolley_draussen := TRUE;

            GOTO mEndeMig;   // Neubeginn

        ELSE   // Schritte 18/19/22..30 und ungueltige Werte
            GOTO mEnde;   // Undefinierter Schritt
    END_CASE;

    GOTO mEnde;   // (nicht erreichbar; alle Zweige enden mit GOTO)

    REGION Schrittketten-Verwaltung (AWL: Ende / Neu_Mig / ENDE_Mig / Plus / tueb / RET)
mEnde:  // NW23 Ende: Undefinierter Schritt
        #Dyn.Sk.Err_Schritt := TRUE;
        #Dyn.Stop_Ursache := 16#A131;
        GOTO mRET;

mNeuMig:   // Neu_Mig: neuen Schritt sofort (im selben Zyklus) aktivieren
        IF #Dyn.Sk.Err_Uebzeit THEN   // Stoerung noch nicht quittiert
            #Dyn.Stop_Ursache := 16#A132;
            GOTO mRET;
        END_IF;
        #Dyn.Sk.Akt_Schritt := #tNeuSchritt;
        #Dyn.Sk.Terr_Zelle := 0;   // Zeitwerte loeschen
        #Dyn.Sk.Tok_Zelle := 0;
        GOTO mNEU;   // zurueck zum Sprungverteiler

mEndeMig:   // NW45: Schrittkette beenden
        #Dyn.Sk.Akt_Schritt := 0;   // Schritt 0: Warten
        #Dyn.Sk.Terr_Zelle := 0;   // Zeitwert loeschen
        #Dyn.Sk.Tok_Zelle := 0;
        #Dyn.ZwischenZiel_eingefahren := 16#0000;

        #Dyn.TrolleyID_Eingefahren := 0;   // Eintrag von Weiche -1
        #Dyn.TrolleyID_Schleuse := 0;
        #Dyn.Trolley_ZwischenZiel := 16#0000;
        #Dyn.Trolley_EndZiel := 16#0000;
        #Dyn.Weichen_Ziel := 16#0000;

        #Dyn.Sk.Err_Uebzeit := FALSE;
        #Dyn.Status.Weiche_geschaltet := FALSE;
        #Dyn.Status."Trolley ausserhalb Weiche" := FALSE;
        #Dyn.Status."Trolley_fährt_rückwärts" := FALSE;
        #Status2."Warten_3.V" := FALSE;
        #Dyn.Error.Err_Kein_LeseErgebnis := FALSE;
        #Status2."2.Lesung_aktiv" := FALSE;
        #Status2."3.Lesung_aktiv" := FALSE;
        #Status2."TrolleyPos_übergeben" := FALSE;
        #Status2.Telegramm_gesendet := FALSE;

        #Dyn.HMI.TrolleyID_Input := 0;   // HMI-Zeug
        GOTO mRET;

mPlus:   // NW46: Naechsten Schritt aktivieren (nach Wartezeit #tWartezeit *10ms)
        #Dyn.Sk.T_ok := #tWartezeit;   // Wartezeit uebergeben

        IF #Dyn.Sk.Err_Uebzeit THEN   // Stoerung noch nicht quittiert
            GOTO mRET;
        END_IF;

        IF #tWartezeit <> 0 THEN   // sonst sofort weiter
            IF #Warte_neu THEN   // Wartezeit neutriggern
                #Dyn.Sk.Tok_Zelle := 0;
            END_IF;
            IF #Warte_stop THEN   // Wartezeit stoppen
                GOTO mRET;
            END_IF;
            #Dyn.Sk.Tok_Zelle := #Dyn.Sk.Tok_Zelle + WORD_TO_INT("MW4");   // Wartezeit +10ms
            IF #Dyn.Sk.Tok_Zelle < #Dyn.Sk.T_ok THEN   // abgelaufen? Nein!
                GOTO mRET;
            END_IF;
        END_IF;

        #Dyn.Sk.Akt_Schritt := #Dyn.Sk.Akt_Schritt + 1;   // Schritt +1
        #Dyn.Sk.Terr_Zelle := 0;   // Zeitwerte loeschen
        #Dyn.Sk.Tok_Zelle := 0;
        GOTO mRET;

mTueb:   // NW47/48: Ueberwachungszeit kontrollieren (#tUebzeit *1s)
        #Dyn.Sk.Ueb_akt := #Ueb_akt;   // Fuer Statuskontrolle
        #Dyn.Sk.T_Err := #tUebzeit;   // Fuer Statuskontrolle

        IF (#tUebzeit > #Dyn.Sk.Terr_Zelle) AND NOT #Dyn.Sk.Err_Uebzeit THEN
            // NW48: Ueberwachungszeit +1
            IF #Dyn.Sk.Ueb_akt AND "1s-Impuls" THEN
                #Dyn.Sk.Terr_Zelle := #Dyn.Sk.Terr_Zelle + 1;   // Ueb.zeit +1s
            END_IF;
            GOTO mRET;
        END_IF;

        IF #Ueb_akt THEN   // Ueberwachungszeit aktiviert
            #Dyn.Sk.Err_Uebzeit := TRUE;
        END_IF;
        #Dyn.Sk.Terr_Zelle := 0;   // Zeitwert loeschen
        GOTO mRET;
    END_REGION

    REGION NW50: Leser-Einheit managen (Outputs schreiben)
mRET:   IF #Leser_Zugriff AND #Schleuse.Parameter_allg.Leser_eingebaut THEN
            #tLeserDb := INT_TO_DINT(#Dyn.SAVE2.Leser_DB);
            #tLeserBase := #Dyn.SAVE2.Leser_DD;

            // Offset 4: Aktuelle Werte holen, Bits setzen, zurueckschreiben
            #tByte := PEEK(area := 16#84, dbNumber := #tLeserDb, byteOffset := #tLeserBase + 4);

            #tByte.%X1 := #Dyn.Leser."OUT".xInStartReading;
            #tByte.%X2 := #Dyn.Leser."OUT".xInHandshakeDone;
            #Dyn.Leser."OUT".xInHandshakeDone := FALSE;
            #tByte.%X4 := #Dyn.Leser."OUT".xInErrQuit;
            #Dyn.Leser."OUT".xInErrQuit := FALSE;
            #tByte.%X5 := #Dyn.Leser."OUT".xInReset;
            #Dyn.Leser."OUT".xInReset := FALSE;

            POKE(area := 16#84, dbNumber := #tLeserDb, byteOffset := #tLeserBase + 4,
                 value := #tByte);   // Veraenderte Werte zurueckschreiben
        END_IF;
    END_REGION
END_FUNCTION_BLOCK
