FUNCTION "FifoIn" : Void
TITLE = Datensatz in FIFO-DB eintragen
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : IBAE
FAMILY : Module
NAME : FifoIn
//SCL-Portierung des AWL-Bausteins FC80 "FifoIn" (verhaltensgleich).
//
//FIFO-Kopf im Schnittstellen-DB ab Byte SSDW:
//  +0  Kopf (Byte, 16#FF = FIFO-Kennung)
//  +1  Datensatzlaenge (Byte, in Worten, 1..128)
//  +2  FifoLaenge (Int, max. Anzahl Datensaetze)
//  +4  Bringzeiger (Int, Byte-Adresse im DB)
//  +6  Holzeiger (Int, Byte-Adresse im DB)
//  +8  Anzahl (Int, eingetragene Datensaetze)
//  +10 Status (Word)
//  +12 Zusatzdaten (4 Worte)
//  +40 Datenbereich, dahinter Endekennung 16#FFEE
//
//ABWEICHUNG ZUM AWL-ORIGINAL:
//  - INDATEN ist jetzt VARIANT statt POINTER (S7-1500/SCL). AWL-/SCL-Aufrufer
//    uebergeben weiterhin einen Bereich (z.B. P#M220.0 WORD 6 bzw. ein Symbol).
//  - Registerindirekte DB-Zugriffe sind durch PEEK/POKE ersetzt (DB-Layout identisch).
//  - Erfolg wird wie bisher ueber ENO/BR gemeldet (Aufrufer: JBI bzw. IF ENO).
   VAR_INPUT
      SSDB : Int;   // Schnittstellen-DB
      SSDW : Int;   // Schnittstellen-1.DW (Byte-Offset des FIFO-Kopfs)
      PARA : Word;   // Aufrufparameter (Auftrag / Anzahl Zusatzdaten)
      INDATEN : Variant;   // InDaten-Schnittstelle (Datensatz, Datensatzlaenge Worte)
   END_VAR

   VAR_OUTPUT
      RE_VAL : Byte;   // Rueckmeldung (Fehlerbits)
   END_VAR

   VAR_TEMP
      Kopf : Byte;   // Fifo-Kennung (FF)
      Datensatzlaenge : Byte;   // Datensatzlaenge in Word (1 - 128)
      FifoLaenge : Int;   // Laenge des Fifo (maximale Anzahl der Datensaetze)
      Bringzeiger : Int;   // Zeiger, wohin naechster Eintrag gespeichert wird
      Holzeiger : Int;   // Zeiger, woher naechste Daten geholt werden
      Anzahl : Int;   // Anzahl der Datensaetze (eingetragene)
      Status : Word;   // Status des Fifo
      Err : Byte;   // Fehlerbits
      Para1 : Byte;   // 1.Aufrufparameter
      Para2 : Byte;   // 2.Aufrufparameter
      Para21 : Byte;   // 2.Aufrufparameter 1.Stelle
      Para22 : Byte;   // 2.Aufrufparameter 2.Stelle
      MinBringzeiger : Int;   // Minimum Bringzeiger
      MaxBringzeiger : Int;   // Maximum Bringzeiger
      SFC_RET_VAL : Int;   // Rueckmeldung TEST_DB-/Serialize-Aufruf
      SFC_DB_LENGTH : Word;   // DB-Laenge in Bytes
      Fifo_Ende : Int;   // Ende des Fifo (Byte-Offset Endekennung)
      Offset : Int;   // Offset 1. zu speicherndes Zusatzwort (Bytes)
      SFC_WRITE_PROT : Bool;   // DB ist schreibgeschuetzt
      NixSave : Bool;   // Status nicht speichern
      tVKE : Bool;   // Ergebnis fuer ENO (BR im AWL)
      tDbNo : DInt;   // DB-Nummer fuer PEEK/POKE
      tAnzWorte : Int;   // Anzahl zu kopierender Worte
      tPos : DInt;   // Serialize-Position
      i : Int;   // Schleifenzaehler
      tPuffer : Array[0..257] of Byte;   // Zwischenpuffer fuer INDATEN
   END_VAR

BEGIN
    REGION NW1: Schnittstellen-DB sinnvoll ?
        #tDbNo := INT_TO_DINT(#SSDB);

        "TEST_DB_TO_ATTR_DB"(DB_Number := INT_TO_WORD(#SSDB),
                             RetVal => #SFC_RET_VAL,
                             DB_Length => #SFC_DB_LENGTH,
                             Write_Prot => #SFC_WRITE_PROT);

        #NixSave := (#SFC_RET_VAL <> 0) OR #SFC_WRITE_PROT;   // Status nicht speichern
        IF #NixSave THEN
            #Err := 16#01;   // kein Fifobereich (AWL: Err0)
            GOTO mErr;
        END_IF;
    END_REGION

    REGION NW2: FIFO-Kopf holen (AWL: Quelle-Pointer + BLKMOV)
        IF #SSDW < 0 OR (#SSDW + 12) > WORD_TO_INT(#SFC_DB_LENGTH) THEN
            #Err.%X1 := TRUE;   // Kopf liegt nicht im DB (AWL: SFC20-Fehler, Err1)
            GOTO mErr;
        END_IF;

        #Kopf            := PEEK(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW));
        #Datensatzlaenge := PEEK(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 1));
        #FifoLaenge  := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 2)));
        #Bringzeiger := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 4)));
        #Holzeiger   := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 6)));
        #Anzahl      := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 8)));
        #Status      := PEEK_WORD(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 10));

        #Err := WORD_TO_BYTE(#Status AND 16#00E0);   // Kapitalfehler aus frueheren Aufrufen?
        IF #Err <> 16#00 THEN
            GOTO mErr;
        END_IF;
    END_REGION

    REGION NW3: Zeigerueberpruefung
        IF #Kopf <> 16#FF THEN   // keine Fifokennung
            #NixSave := TRUE;
            #Err := 16#01;   // kein Fifobereich
            GOTO mErr;
        END_IF;

        IF BYTE_TO_INT(#Datensatzlaenge) < 1 OR BYTE_TO_INT(#Datensatzlaenge) > 128 THEN
            #Err := 16#01;   // kein Fifobereich
            GOTO mErr;
        END_IF;

        IF #FifoLaenge < 1 THEN
            #Err := 16#01;   // kein Fifobereich
            GOTO mErr;
        END_IF;

        #MinBringzeiger := #SSDW + 40;   // Speichern ab 20.DW
        #MaxBringzeiger := (#FifoLaenge - 1) * BYTE_TO_INT(#Datensatzlaenge) * 2 + 40 + #SSDW;
        #Fifo_Ende := BYTE_TO_INT(#Datensatzlaenge) * 2 + #MaxBringzeiger;   // Zeiger Endekennung

        IF #Fifo_Ende > WORD_TO_INT(#SFC_DB_LENGTH) THEN   // Fifo-DB ist zu kurz!
            #Err := 16#01;   // kein Fifo-Bereich
            GOTO mErr;
        END_IF;

        IF PEEK_WORD(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#Fifo_Ende)) <> 16#FFEE THEN
            #Err := 16#01;   // kein Fifobereich (Endekennung fehlt)
            GOTO mErr;
        END_IF;

        IF #MinBringzeiger > #Bringzeiger THEN
            // Nur zulaessig, wenn alles in Grundstellung: dann initialisieren
            IF #Bringzeiger <> 0 OR #Holzeiger <> 0 OR #Anzahl <> 0 THEN
                #Err.%X5 := TRUE;   // Bringzeiger ist ausserhalb
                GOTO mErr;
            END_IF;
            #Bringzeiger := #MinBringzeiger;
            #Holzeiger := #MinBringzeiger;
        END_IF;

        IF #Bringzeiger > #MaxBringzeiger THEN
            #Err.%X5 := TRUE;   // Bringzeiger ist ausserhalb
            GOTO mErr;
        END_IF;

        IF (#Bringzeiger = #Holzeiger) AND (#FifoLaenge <> #Anzahl) AND (#Anzahl <> 0) THEN
            #Err.%X7 := TRUE;   // Versatz Bring-/Holzeiger
            GOTO mErr;
        END_IF;
        IF (#Bringzeiger <> #Holzeiger) AND ((#FifoLaenge = #Anzahl) OR (#Anzahl = 0)) THEN
            #Err.%X7 := TRUE;   // Versatz Bring-/Holzeiger
            GOTO mErr;
        END_IF;
    END_REGION

    REGION NW4: Nur Zusatzdaten / als 1.Datensatz speichern ?
        #Para1 := WORD_TO_BYTE(SHR(IN := #PARA, N := 8));   // 1.Aufrufparameter
        #Para2 := WORD_TO_BYTE(#PARA);   // 2.Aufrufparameter

        IF #Para1 = 16#02 THEN   // Nur Zusatzdaten speichern
            IF #Para2 = 16#00 THEN
                #Err.%X3 := TRUE;   // Falscher Aufrufparameter
                GOTO mErr;
            END_IF;
            GOTO mNw06;   // Zusatzdaten speichern
        END_IF;

        IF #Para1 = 16#01 THEN   // Als 1.Datensatz speichern
            #Bringzeiger := #MinBringzeiger;
            #Holzeiger := #MinBringzeiger;
            #Anzahl := 0;
            GOTO mNw06;   // Zusatz-Daten speichern?
        END_IF;
    END_REGION

    REGION NW5: Hat Fifo Platz / Datenbereich leer ?
        IF #Anzahl >= #FifoLaenge THEN
            IF #Anzahl = #FifoLaenge THEN
                #Err.%X2 := TRUE;   // Fifo ist voll
            ELSE
                #Err.%X7 := TRUE;   // Versatz Bring-/Holzeiger
            END_IF;
            GOTO mErr;
        END_IF;

        // Ziel-Speicherzellen muessen leer (0) sein
        FOR #i := 0 TO BYTE_TO_INT(#Datensatzlaenge) - 1 DO
            IF PEEK_WORD(area := 16#84, dbNumber := #tDbNo,
                         byteOffset := INT_TO_DINT(#Bringzeiger + #i * 2)) <> 16#0000 THEN
                #Err.%X4 := TRUE;   // Speicherzelle ist belegt
                GOTO mErr;
            END_IF;
        END_FOR;
    END_REGION

    REGION NW6: Zusatzdaten schreiben (Quelle: Merker ab MB240)
mNw06:  IF #Para2 <> 16#00 THEN
            #Para21 := SHR(IN := #Para2 AND 16#F0, N := 4);   // 1.Stelle isolieren (von)
            #Para22 := #Para2 AND 16#0F;   // 2.Stelle isolieren (bis)

            IF (#Para21 > #Para22)                    // 1.Stelle > 2.Stelle !
               OR (#Para22 > 16#04)                   // 2.Stelle > 4 !
               OR (#Para21 = 16#00) THEN              // 1.Stelle = 0 !
                #Err.%X3 := TRUE;   // Falscher Aufrufparameter
                GOTO mErr;
            END_IF;

            #tAnzWorte := BYTE_TO_INT(#Para22) - BYTE_TO_INT(#Para21) + 1;
            #Offset := (BYTE_TO_INT(#Para21) - 1) * 2;   // Offset 1. zu speicherndes Zusatzwort

            // Quelle: MB240+Offset -> Ziel: DB ab SSDW+12+Offset (ab 6.DW)
            FOR #i := 0 TO #tAnzWorte * 2 - 1 DO
                POKE(area := 16#84, dbNumber := #tDbNo,
                     byteOffset := INT_TO_DINT(#SSDW + 12 + #Offset + #i),
                     value := PEEK(area := 16#83, dbNumber := 0,
                                   byteOffset := INT_TO_DINT(240 + #Offset + #i)));
            END_FOR;
        END_IF;
    END_REGION

    REGION NW7: Fifodaten schreiben
        IF #Para1 = 16#FF THEN   // Testsendung: Zeiger nur testweise veraendern
            GOTO mNw08;
        END_IF;
        IF #Para1 = 16#02 THEN   // Nur Zusatzdaten speichern: fertig!
            GOTO mEnde;
        END_IF;

        // INDATEN in Zwischenpuffer holen (AWL: BLKMOV ueber uebergebenen Pointer)
        #tPos := 0;
        #SFC_RET_VAL := Serialize(SRC_VARIANT := #INDATEN, DEST_ARRAY := #tPuffer, POS := #tPos);
        IF (#SFC_RET_VAL <> 0) OR (#tPos < BYTE_TO_INT(#Datensatzlaenge) * 2) THEN
            #Err.%X1 := TRUE;   // Kopier-Fehler (AWL: SFC20-Aufruf mit Fehler)
            GOTO mErr;
        END_IF;

        // Datensatz ab Bringzeiger in FIFO-DB schreiben
        FOR #i := 0 TO BYTE_TO_INT(#Datensatzlaenge) * 2 - 1 DO
            POKE(area := 16#84, dbNumber := #tDbNo,
                 byteOffset := INT_TO_DINT(#Bringzeiger + #i),
                 value := #tPuffer[#i]);
        END_FOR;
    END_REGION

    REGION NW8: Bringzeiger, Anzahl erhoehen
mNw08:  #Bringzeiger := BYTE_TO_INT(#Datensatzlaenge) * 2 + #Bringzeiger;
        IF #Bringzeiger > #MaxBringzeiger THEN   // Maximum erreicht?
            #Bringzeiger := #MinBringzeiger;
        END_IF;

        #Anzahl := #Anzahl + 1;
    END_REGION

    REGION NW9: Versatz Bring-/Holzeiger
        IF (#Bringzeiger = #Holzeiger) AND (#FifoLaenge <> #Anzahl) THEN
            #Err.%X7 := TRUE;   // Versatz Bring-/Holzeiger
            GOTO mErr;
        END_IF;
        IF (#Bringzeiger <> #Holzeiger) AND (#FifoLaenge = #Anzahl) THEN
            #Err.%X7 := TRUE;   // Versatz Bring-/Holzeiger
            GOTO mErr;
        END_IF;
    END_REGION

    REGION NW10: Fehlerausgabe / RET_VAL
mEnde:  #tVKE := TRUE;   // VKE = 1 ohne Fehler !!!
        GOTO mOk;

mErr:   #tVKE := FALSE;   // VKE = 0 bei Fehler !!!

mOk:    #RE_VAL := #Err;
    END_REGION

    #Status := #Status OR BYTE_TO_WORD(#Err);

    REGION NW11: Fifokopfdaten speichern
        IF #Err.%X0 AND #NixSave THEN   // kein gueltiger Fifobereich: nichts speichern
            ENO := #tVKE;
            RETURN;
        END_IF;

        "MW254" := INT_TO_WORD(#Anzahl - 1);   // Uebergabe an FC+1 (da vorher zum Test erhoeht wurde)

        IF #Para1 = 16#FF THEN   // Testsendung: Zeiger wurden nur testweise geaendert!
            ENO := #tVKE;
            RETURN;
        END_IF;

        // Bringzeiger, Holzeiger, Anzahl, Status in FIFO-Kopf zuruecksichern (ab SSDW+4)
        POKE(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 4), value := INT_TO_WORD(#Bringzeiger));
        POKE(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 6), value := INT_TO_WORD(#Holzeiger));
        POKE(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 8), value := INT_TO_WORD(#Anzahl));
        POKE(area := 16#84, dbNumber := #tDbNo, byteOffset := INT_TO_DINT(#SSDW + 10), value := #Status);

        "MW248" := INT_TO_WORD(#FifoLaenge);    // Uebergabe an FC+1 (fuer Durchlaufspeicher)
        "MW250" := INT_TO_WORD(#Bringzeiger);   // Uebergabe an FC+1 (fuer Durchlaufspeicher)
        "MW252" := INT_TO_WORD(#Holzeiger);     // Uebergabe an FC+1 (fuer Durchlaufspeicher)
        "MW254" := INT_TO_WORD(#Anzahl);        // Uebergabe an FC+1 (fuer Durchlaufspeicher)
    END_REGION

    ENO := #tVKE;   // AWL: SAVE (BR-Bit fuer JBI beim Aufrufer)
END_FUNCTION
