erste Fassung der Konvertierung der xml Dateien in .scl Code aus dem Projekt von Andi Emmert
This commit is contained in:
@@ -0,0 +1,460 @@
|
||||
FUNCTION "DatenOut" : Void
|
||||
TITLE = Datensatz in Datenliste suchen / lesen / loeschen
|
||||
{ S7_Optimized_Access := 'FALSE' }
|
||||
AUTHOR : IBAE
|
||||
FAMILY : Module
|
||||
NAME : DatenOut
|
||||
//SCL-Portierung des AWL-Bausteins FC86 "DatenOut" (verhaltensgleich).
|
||||
//
|
||||
//Datenlisten-Kopf im Datenlisten-DB ab Byte SSDW:
|
||||
// +0 Kopf (Byte, 16#DA = Datenliste, 16#DB = Sort-Datenliste)
|
||||
// +1 Datensatzlaenge (Byte, in Worten, 1..128)
|
||||
// +2 Listenlaenge (Int, max. Anzahl Datensaetze)
|
||||
// +4 Stammdaten (Int, Anzahl Suchworte 1..9)
|
||||
// +6 IndDB (Word, indizierter DB; nur bei Stammdaten=1)
|
||||
// +8 IndDB_Laenge (Int, Laenge indizierter DB in Eintraegen)
|
||||
// +10 IndDaten_FC (Int, Wandel-FC-Nummer fuer indizierte Suche)
|
||||
// +12 Suchzeiger / letzter Zugriff (Int, Byte-Adresse)
|
||||
// +16 AnzahlEintraege (Int)
|
||||
// +20 Datenbereich, dahinter Endekennung 16#DAEE bzw. 16#DBEE
|
||||
//
|
||||
//Aufrufparameter PARA (High-Byte = Para1, Low-Byte = Para2):
|
||||
// Para1: 00=suchen, E1=suchen+loeschen, F1=1.Datensatz holen,
|
||||
// FF=naechsten Datensatz holen, 02..80=Offset-Suche ab Wort X
|
||||
// Para2: 00=Stammdaten aus Kopf, sonst Anzahl Stammdaten (1..9)
|
||||
//
|
||||
//RE_VAL: Bit0=kein Datenlistenbereich, Bit1=Kopier-/SFC-Fehler, Bit2=Listenende,
|
||||
// Bit3=falscher Parameter, Bit4=undef. Suchdaten, Bit5=undef. OutDaten,
|
||||
// Bit6=Datensatz nicht vorhanden, Bit7=Daten vorhanden. Erfolg zusaetzlich via ENO/BR.
|
||||
//
|
||||
//ABWEICHUNGEN ZUM AWL-ORIGINAL:
|
||||
// - SEARCHDATEN/OUTDATEN sind VARIANT statt POINTER (S7-1500/SCL).
|
||||
// SEARCHDATEN muss mind. Stammdaten*2 Bytes referenzieren.
|
||||
// OUTDATEN sollte exakt Datensatzlaenge*2 Bytes gross sein (groessere Ziele
|
||||
// werden hinter dem Datensatz mit 0 aufgefuellt).
|
||||
// - Die DB-Laengenpruefung des Such-/Out-DB (AWL NW5/NW8) entfaellt, da der
|
||||
// Variant-Zugriff typsicher ist. Fehlerbit 5 (undef. OutDaten) wird nicht mehr erzeugt.
|
||||
// - Der indirekte Aufruf UC FC[#Wandel_FC] ist als CASE ausgefuehrt
|
||||
// (aktuell einziger Wandel-FC im Projekt: FC1000 "Wandel_FC_fuer_DB1000").
|
||||
// - Position wird mit 0 initialisiert (AWL: undefiniert, wenn nicht gefunden).
|
||||
VAR_INPUT
|
||||
SSDB : Int; // Datenlisten-DB
|
||||
SSDW : Int; // Datenlisten-1.DW (Byte-Offset des Kopfs)
|
||||
PARA : Word; // Aufrufparameter (Auftrag / Anzahl Stammdaten)
|
||||
SEARCHDATEN : Variant; // Such-Daten-Schnittstelle (Stammdaten Worte)
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
RE_VAL : Byte; // Rueckmeldung (Fehler-/Statusbits)
|
||||
Position : Int; // Daten stehen an angegebener Position in Datenliste
|
||||
END_VAR
|
||||
|
||||
VAR_IN_OUT
|
||||
OUTDATEN : Variant; // OutDaten-Schnittstelle (Datensatzlaenge Worte)
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
Kopf : Byte; // Datenliste-Kennung (DA/DB)
|
||||
Datensatzlaenge : Int; // Datensatzlaenge in Word (1..128)
|
||||
Listenlaenge : Int; // Laenge der Datenliste (max. Anzahl Datensaetze)
|
||||
Stammdaten : Int; // Stammdatenlaenge in Word (1..9)
|
||||
IndDB : Word; // Indizierter DB (nur bei Stammdaten = 1 erlaubt !!!)
|
||||
IndDB_Laenge : Int; // Laenge des indizierten DB in Eintraegen
|
||||
IndDaten_FC : Int; // FC zum Wandeln der indizierten Daten
|
||||
Suchzeiger : Int; // Speicherung des letzten Zugriffs / Suchzeiger (Byte-Adresse)
|
||||
Para1 : Byte; // 1.Aufrufparameter
|
||||
Para2 : Byte; // 2.Aufrufparameter
|
||||
OffsetDaten_1 : Int; // Stammdaten beginnen ab hier (Byte-Offset)
|
||||
SFC_RET_VAL : Int; // Rueckmeldung SFC-/Serialize-Aufruf
|
||||
SFC_DB_LENGTH : Word; // Rueckmeldung TEST_DB-Aufruf
|
||||
SFC_WRITE_PROT : Bool; // Rueckmeldung TEST_DB-Aufruf (letzter Aufruf!)
|
||||
DB_Laenge : Int; // DB-Laenge nach BCD-Wandlung (wie AWL)
|
||||
Datenlisten_Ende : Int; // Ende der Datenliste (Byte-Offset Endekennung)
|
||||
Datenblock_inBytes : Int; // Datenblocklaenge in Bytes
|
||||
Daten_1 : Word; // 1.Datenwort der Suchdaten
|
||||
Daten_2 : DWord; // 1.+2.Datenwort der Suchdaten (fuer Wandel-FC)
|
||||
Wandel_FC : Int; // Wandel-FC-Nummer
|
||||
Suchstelle : Int; // Bei angegebenem Datensatz suchen (Position)
|
||||
LoeschPos : Int; // Loesch-Position
|
||||
Indok : Bool; // Indizierung moeglich
|
||||
IndZeiger_ok : Bool; // Indizier-Zeiger angegeben
|
||||
Weitersuchen : Bool; // Weitersuchen ist aktiv (Para1 F1/FF)
|
||||
Offsetsuchen : Bool; // Offsetsuchen ist aktiv
|
||||
Sortliste : Bool; // Sortliste wird durchsucht
|
||||
tRE_VAL : Byte; // Rueckmeldung (Zwischenwert)
|
||||
tVKE : Bool; // Ergebnis fuer ENO (BR im AWL)
|
||||
tGefunden : Bool; // Vergleichs-/Suchergebnis
|
||||
tDbList : DInt; // Datenlisten-DB-Nummer fuer PEEK/POKE
|
||||
tDbInd : DInt; // Indizierter DB fuer PEEK/POKE
|
||||
tIndex : Int; // Indexwert fuer indizierte Suche
|
||||
IndByteOfs : Int; // Byte-Offset des Index-Eintrags im IndDB
|
||||
tWort : Word; // Wort-Zwischenwert
|
||||
tAnz : Int; // Anzahl-Zwischenwert
|
||||
tAnzBytes : Int; // Anzahl zu verschiebender Bytes
|
||||
tPos : DInt; // Serialize-/Deserialize-Position
|
||||
i : Int; // Schleifenzaehler
|
||||
tSuchPuffer : Array[0..257] of Byte; // Zwischenpuffer SEARCHDATEN
|
||||
tRecPuffer : Array[0..257] of Byte; // Zwischenpuffer Datensatz -> OUTDATEN
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
#tRE_VAL := 16#00;
|
||||
#Suchstelle := 0;
|
||||
#Position := 0;
|
||||
|
||||
REGION NW1: Schnittstellen-DB sinnvoll ?
|
||||
#tDbList := 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);
|
||||
"BCD (4stellig)_to_INT"(BCD_Wert := #SFC_DB_LENGTH, Int_Wert => #DB_Laenge);
|
||||
|
||||
IF (#SFC_RET_VAL <> 0) OR #SFC_WRITE_PROT THEN // schreibgeschuetzt
|
||||
GOTO mErr0; // kein Datenlisten-Bereich
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW2: Datenlisten-Kopf holen (AWL: Quelle-Pointer + BLKMOV, 10 Worte)
|
||||
IF #SSDW < 0 OR (#SSDW + 20) > #DB_Laenge THEN
|
||||
GOTO mErr1; // Kopf liegt nicht im DB (AWL: SFC20-Fehler)
|
||||
END_IF;
|
||||
|
||||
#Kopf := PEEK(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW));
|
||||
#Datensatzlaenge := BYTE_TO_INT(PEEK(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 1)));
|
||||
#Listenlaenge := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 2)));
|
||||
#Stammdaten := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 4)));
|
||||
#IndDB := PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 6));
|
||||
#IndDB_Laenge := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 8)));
|
||||
#IndDaten_FC := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 10)));
|
||||
#Suchzeiger := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 12)));
|
||||
END_REGION
|
||||
|
||||
REGION NW3: Zeigerueberpruefung
|
||||
#Sortliste := (#Kopf = 16#DB); // Sort-Datenlistenkennung ?
|
||||
IF NOT #Sortliste AND (#Kopf <> 16#DA) THEN // Datenlistenkennung ?
|
||||
GOTO mErr0; // kein Datenlistenbereich
|
||||
END_IF;
|
||||
|
||||
IF (#Datensatzlaenge < 1) OR (#Datensatzlaenge > 128)
|
||||
OR (#Listenlaenge < 1)
|
||||
OR (#Stammdaten < 1) OR (#Stammdaten > 9)
|
||||
OR (#Stammdaten > #Datensatzlaenge) THEN
|
||||
GOTO mErr0; // kein Datenlistenbereich
|
||||
END_IF;
|
||||
|
||||
#Datenlisten_Ende := #Listenlaenge * #Datensatzlaenge * 2 + 20 + #SSDW;
|
||||
|
||||
IF #Datenlisten_Ende > #DB_Laenge THEN // Datenlisten-DB ist zu kurz!
|
||||
GOTO mErr0; // kein Datenlisten-Bereich
|
||||
END_IF;
|
||||
|
||||
#tWort := PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#Datenlisten_Ende));
|
||||
IF (#tWort <> 16#DAEE) AND (#tWort <> 16#DBEE) THEN // Endekennung ?
|
||||
GOTO mErr0; // kein Datenlisten-Bereich
|
||||
END_IF;
|
||||
|
||||
#Datenblock_inBytes := #Datensatzlaenge * 2;
|
||||
END_REGION
|
||||
|
||||
REGION NW4: Auftrag kontrollieren
|
||||
#Indok := FALSE;
|
||||
#IndZeiger_ok := FALSE;
|
||||
#Weitersuchen := FALSE;
|
||||
#Offsetsuchen := FALSE;
|
||||
#OffsetDaten_1 := 0; // Vorerst kein Offset
|
||||
|
||||
#Para1 := WORD_TO_BYTE(SHR(IN := #PARA, N := 8));
|
||||
#Para2 := WORD_TO_BYTE(#PARA);
|
||||
|
||||
IF #Para1 = 16#00 THEN // Normale Suche
|
||||
GOTO mW401;
|
||||
END_IF;
|
||||
|
||||
IF #Para1 = 16#E1 THEN // Suchen + loeschen
|
||||
IF #Sortliste THEN
|
||||
GOTO mErr3; // Austragen aus Sortliste nicht erlaubt !
|
||||
END_IF;
|
||||
GOTO mW401;
|
||||
END_IF;
|
||||
|
||||
IF #Para1 = 16#F1 THEN // 1.Datensatz holen
|
||||
IF (#Para2 <> 16#00) OR #Sortliste THEN
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
#Weitersuchen := TRUE;
|
||||
#Suchzeiger := #SSDW + 20;
|
||||
GOTO mNw08; // OutDaten ok ?
|
||||
END_IF;
|
||||
|
||||
IF #Para1 = 16#FF THEN // Naechsten Datensatz holen
|
||||
IF (#Para2 <> 16#00) OR #Sortliste THEN
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
#Weitersuchen := TRUE;
|
||||
#Suchzeiger := #Suchzeiger + #Datenblock_inBytes; // Naechsten Datensatz suchen
|
||||
IF (#Datenblock_inBytes * #Listenlaenge + #SSDW + 20) > #Suchzeiger THEN
|
||||
GOTO mNw08; // Noch nicht am Ende
|
||||
END_IF;
|
||||
GOTO mErr2; // Listenende erreicht
|
||||
END_IF;
|
||||
|
||||
// Offset-Suche: Para1 = Offset des 1.Stammdatenworts (2..128)
|
||||
IF (BYTE_TO_INT(#Para1) < 2)
|
||||
OR (BYTE_TO_INT(#Para1) > 128)
|
||||
OR (BYTE_TO_INT(#Para1) > #Datensatzlaenge) THEN
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
#OffsetDaten_1 := (BYTE_TO_INT(#Para1) - 1) * 2; // Richtiger Offset der 1.Stammdaten
|
||||
#Suchzeiger := 0; // Suche ab Anfang
|
||||
#Offsetsuchen := TRUE;
|
||||
|
||||
mW401: IF #Para2 <> 16#00 THEN
|
||||
#Stammdaten := BYTE_TO_INT(#Para2); // Anzahl der Stammdaten aus Aufruf
|
||||
IF (#Stammdaten > #Datensatzlaenge)
|
||||
OR (#Stammdaten < 1)
|
||||
OR (#Stammdaten > 9) THEN
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
END_IF;
|
||||
|
||||
IF #Offsetsuchen THEN
|
||||
IF (BYTE_TO_INT(#Para1) - 1 + #Stammdaten) > #Datensatzlaenge THEN // Letzte Stammdaten zu gross ?
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW5: Such-Daten uebernehmen (AWL: Pointer-Parsing)
|
||||
#Suchzeiger := 0;
|
||||
|
||||
#tPos := 0;
|
||||
#SFC_RET_VAL := Serialize(SRC_VARIANT := #SEARCHDATEN, DEST_ARRAY := #tSuchPuffer, POS := #tPos);
|
||||
IF (#SFC_RET_VAL <> 0) OR (#tPos < INT_TO_DINT(#Stammdaten * 2)) THEN
|
||||
GOTO mErr4; // Undefinierte Suchdaten
|
||||
END_IF;
|
||||
|
||||
#Daten_1 := SHL(IN := BYTE_TO_WORD(#tSuchPuffer[0]), N := 8) OR BYTE_TO_WORD(#tSuchPuffer[1]);
|
||||
#Daten_2 := SHL(IN := BYTE_TO_DWORD(#tSuchPuffer[0]), N := 24)
|
||||
OR SHL(IN := BYTE_TO_DWORD(#tSuchPuffer[1]), N := 16)
|
||||
OR SHL(IN := BYTE_TO_DWORD(#tSuchPuffer[2]), N := 8)
|
||||
OR BYTE_TO_DWORD(#tSuchPuffer[3]);
|
||||
|
||||
// Mindestens ein Suchwort <> 0 ?
|
||||
#tGefunden := FALSE;
|
||||
FOR #i := 0 TO #Stammdaten - 1 DO
|
||||
IF (#tSuchPuffer[#i * 2] <> 16#00) OR (#tSuchPuffer[#i * 2 + 1] <> 16#00) THEN
|
||||
#tGefunden := TRUE;
|
||||
EXIT;
|
||||
END_IF;
|
||||
END_FOR;
|
||||
IF NOT #tGefunden THEN
|
||||
GOTO mErr4; // Undefinierte Stammdaten
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW6: Indiziert suchen ?
|
||||
IF (#IndDB <> 16#0000) AND NOT #Offsetsuchen AND NOT #Sortliste THEN
|
||||
"TEST_DB_TO_ATTR_DB"(DB_Number := #IndDB,
|
||||
RetVal => #SFC_RET_VAL,
|
||||
DB_Length => #SFC_DB_LENGTH,
|
||||
Write_Prot => #SFC_WRITE_PROT);
|
||||
"BCD (4stellig)_to_INT"(BCD_Wert := #SFC_DB_LENGTH, Int_Wert => #DB_Laenge);
|
||||
|
||||
IF (#SFC_RET_VAL = 0) AND NOT #SFC_WRITE_PROT // Indizier-DB vorhanden
|
||||
AND NOT (#IndDB_Laenge * 2 > #DB_Laenge) // Indizier-DB lang genug
|
||||
AND (#Stammdaten < 3) THEN // nicht zu viele Stammdaten
|
||||
#Wandel_FC := #IndDaten_FC;
|
||||
IF #Wandel_FC > 0 THEN
|
||||
"MW90" := #Daten_1; // Stammdaten (1.DW) uebergeben
|
||||
"MD94" := #Daten_2; // Stammdaten (1.+2.DW) uebergeben
|
||||
CASE #Wandel_FC OF // AWL: UC FC[#Wandel_FC]
|
||||
1000:
|
||||
"Wandel_FC_für_DB1000"();
|
||||
ELSE
|
||||
; // unbekannter Wandel-FC: MW92 bleibt unveraendert
|
||||
END_CASE;
|
||||
#tIndex := WORD_TO_INT("MW92"); // Rueckwert vom FC
|
||||
ELSE
|
||||
#tIndex := WORD_TO_INT(#Daten_1); // Stammdaten direkt als Index
|
||||
END_IF;
|
||||
|
||||
IF (#tIndex >= 0) AND (#tIndex < #IndDB_Laenge) THEN // Zeiger im Ind.DB ?
|
||||
#IndByteOfs := #tIndex * 2; // DB byteorientiert
|
||||
#tDbInd := INT_TO_DINT(WORD_TO_INT(#IndDB));
|
||||
#Suchzeiger := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbInd,
|
||||
byteOffset := INT_TO_DINT(#IndByteOfs)));
|
||||
#Indok := TRUE; // Indizierung moeglich
|
||||
#IndZeiger_ok := TRUE; // Indizier-Zeiger angegeben
|
||||
END_IF;
|
||||
END_IF;
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW7: Kontrolle auf gleiche Stammdaten (Suchschleife)
|
||||
IF (#Suchzeiger = 0)
|
||||
OR (((#Suchzeiger - 20) MOD #Datenblock_inBytes) <> 0) THEN // Suchzeiger nicht im Raster
|
||||
// Suche bei 1.Datensatz beginnen (AWL: W700)
|
||||
#Suchstelle := 1;
|
||||
#IndZeiger_ok := FALSE;
|
||||
#Suchzeiger := #SSDW + 20;
|
||||
ELSE
|
||||
#Suchstelle := 0; // Bei angegebener Stelle suchen
|
||||
END_IF;
|
||||
|
||||
REPEAT
|
||||
// Stammdaten vergleichen (AWL: W701/L700-Schleife)
|
||||
#tGefunden := TRUE;
|
||||
FOR #i := 0 TO #Stammdaten - 1 DO
|
||||
#tWort := SHL(IN := BYTE_TO_WORD(#tSuchPuffer[#i * 2]), N := 8)
|
||||
OR BYTE_TO_WORD(#tSuchPuffer[#i * 2 + 1]);
|
||||
IF #tWort <> PEEK_WORD(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #OffsetDaten_1 + #i * 2)) THEN
|
||||
#tGefunden := FALSE;
|
||||
EXIT;
|
||||
END_IF;
|
||||
END_FOR;
|
||||
|
||||
IF #tGefunden THEN
|
||||
#tRE_VAL := 16#80; // Daten sind vorhanden
|
||||
GOTO mNw08; // Datensatz ist in Liste enthalten
|
||||
END_IF;
|
||||
|
||||
// AWL: W703
|
||||
IF #Suchstelle = 0 THEN
|
||||
// Suche bei angegebener Stelle erfolglos: ab 1.Datensatz neu (AWL: W700)
|
||||
#Suchstelle := 1;
|
||||
#IndZeiger_ok := FALSE;
|
||||
#Suchzeiger := #SSDW + 20;
|
||||
ELSE
|
||||
#Suchstelle := #Suchstelle + 1;
|
||||
IF #Suchstelle > #Listenlaenge THEN // Alle Datensaetze durchsucht
|
||||
GOTO mErr6; // Datensatz ist nicht vorhanden
|
||||
END_IF;
|
||||
#Suchzeiger := #Suchzeiger + #Datenblock_inBytes; // AWL: W705
|
||||
END_IF;
|
||||
UNTIL FALSE END_REPEAT;
|
||||
END_REGION
|
||||
|
||||
REGION NW8+NW9: Datensatz an OUTDATEN uebergeben
|
||||
mNw08: FILL_BLK(IN := BYTE#16#00, COUNT := 258, OUT => #tRecPuffer[0]);
|
||||
|
||||
FOR #i := 0 TO #Datensatzlaenge * 2 - 1 DO
|
||||
#tRecPuffer[#i] := PEEK(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #i));
|
||||
END_FOR;
|
||||
|
||||
#tPos := 0;
|
||||
#SFC_RET_VAL := Deserialize(SRC_ARRAY := #tRecPuffer, DEST_VARIANT := #OUTDATEN, POS := #tPos);
|
||||
IF #SFC_RET_VAL <> 0 THEN
|
||||
GOTO mErr1; // Kopier-Fehler (AWL: SFC20-Aufruf mit Fehler)
|
||||
END_IF;
|
||||
|
||||
IF NOT #SFC_WRITE_PROT THEN // Datenliste kann beschrieben werden
|
||||
// Speicherung letzter Zugriff
|
||||
POKE(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 12),
|
||||
value := INT_TO_WORD(#Suchzeiger));
|
||||
#Position := #Suchstelle; // Uebergabe nach aussen
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW10+NW11: Daten aus Datenliste loeschen ?
|
||||
IF (#Para1 = 16#E1) AND NOT #SFC_WRITE_PROT THEN // Daten loeschen ?
|
||||
IF #Indok THEN
|
||||
// NW11: Bei ind. Zugriff nur loeschen, nicht verschieben, keine Anzahl!
|
||||
FOR #i := 0 TO #Datensatzlaenge - 1 DO
|
||||
POKE(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #i * 2), value := WORD#16#0000);
|
||||
END_FOR;
|
||||
POKE(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 12),
|
||||
value := WORD#16#0000); // letzter Zugriff loeschen
|
||||
#Suchzeiger := 0;
|
||||
#IndZeiger_ok := FALSE;
|
||||
ELSE
|
||||
// NW10: loeschen + nachfolgende Datensaetze vorschieben
|
||||
#LoeschPos := (#Suchzeiger - 20 - #SSDW) / #Datenblock_inBytes + 1; // Stelle, die geloescht wird
|
||||
IF #LoeschPos <> #Listenlaenge THEN // nicht der letzte Eintrag
|
||||
// Daten verschieben (AWL: BLKMOV; hier bytewise vorwaerts = sicher bei Ueberlappung)
|
||||
#tAnzBytes := (#Listenlaenge - #LoeschPos) * #Datenblock_inBytes;
|
||||
FOR #i := 0 TO #tAnzBytes - 1 DO
|
||||
POKE(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #i),
|
||||
value := PEEK(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #Datenblock_inBytes + #i)));
|
||||
END_FOR;
|
||||
END_IF;
|
||||
|
||||
// Letzten Eintrag loeschen (AWL: Last)
|
||||
FOR #i := 0 TO #Datensatzlaenge - 1 DO
|
||||
POKE(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Datenlisten_Ende - #Datenblock_inBytes + #i * 2),
|
||||
value := WORD#16#0000);
|
||||
END_FOR;
|
||||
|
||||
// Speicherung letzter Zugriff loeschen
|
||||
POKE(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 12),
|
||||
value := WORD#16#0000);
|
||||
#Suchzeiger := 0;
|
||||
#IndZeiger_ok := FALSE;
|
||||
|
||||
// Anzahl Eintraege -1 (Minimum 0)
|
||||
#tAnz := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#SSDW + 16))) - 1;
|
||||
IF #tAnz < 0 THEN
|
||||
#tAnz := 0; // Minimum uebergeben
|
||||
END_IF;
|
||||
POKE(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 16),
|
||||
value := INT_TO_WORD(#tAnz));
|
||||
END_IF;
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW12: Weitersuchen aktiv ? Datensatz vorhanden oder 0 ?
|
||||
IF #Weitersuchen THEN
|
||||
#tGefunden := FALSE;
|
||||
FOR #i := 0 TO #Datensatzlaenge - 1 DO
|
||||
IF (#tRecPuffer[#i * 2] <> 16#00) OR (#tRecPuffer[#i * 2 + 1] <> 16#00) THEN
|
||||
#tGefunden := TRUE; // Etwas angegeben
|
||||
EXIT;
|
||||
END_IF;
|
||||
END_FOR;
|
||||
IF #tGefunden THEN
|
||||
#tRE_VAL := 16#80; // Daten sind vorhanden
|
||||
ELSE
|
||||
#tRE_VAL := 16#40; // Daten sind nicht vorhanden
|
||||
END_IF;
|
||||
GOTO mEnde;
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW13: Indizierung ok ?
|
||||
IF #Indok AND NOT #IndZeiger_ok THEN
|
||||
// Index-Eintrag im indizierten DB aktualisieren
|
||||
POKE(area := 16#84, dbNumber := #tDbInd, byteOffset := INT_TO_DINT(#IndByteOfs),
|
||||
value := INT_TO_WORD(#Suchzeiger));
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW14+NW15: Fehlerausgabe / RET_VAL
|
||||
mEnde: #tVKE := TRUE; // VKE = 1 ohne Fehler !!!
|
||||
GOTO mOk;
|
||||
|
||||
mErr0: #tRE_VAL := 16#01; // Kein Datenlistenbereich
|
||||
GOTO mErrX;
|
||||
mErr1: #tRE_VAL := 16#02; // Kopier-/SFC-Fehler
|
||||
GOTO mErrX;
|
||||
mErr2: #tRE_VAL := 16#04; // Listenende ist erreicht
|
||||
GOTO mErrX;
|
||||
mErr3: #tRE_VAL := 16#08; // Falscher Parameter bei Aufruf
|
||||
GOTO mErrX;
|
||||
mErr4: #tRE_VAL := 16#10; // Undefinierte Suchdaten
|
||||
GOTO mErrX;
|
||||
mErr6: #tRE_VAL := 16#40; // Datensatz nicht vorhanden
|
||||
|
||||
mErrX: #tVKE := FALSE; // VKE = 0 bei Fehler !!!
|
||||
|
||||
mOk: #RE_VAL := #tRE_VAL;
|
||||
ENO := #tVKE; // AWL: SAVE (BR-Bit fuer JBI beim Aufrufer)
|
||||
END_REGION
|
||||
END_FUNCTION
|
||||
@@ -0,0 +1,303 @@
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,146 @@
|
||||
FUNCTION_BLOCK "MZylinder"
|
||||
TITLE = Zylinderansteuerung
|
||||
{ S7_Optimized_Access := 'FALSE' }
|
||||
AUTHOR : IBAE
|
||||
FAMILY : Module
|
||||
NAME : MZylinder
|
||||
//SCL-Portierung des AWL-Bausteins FB92 "MZylinder" (verhaltensgleich).
|
||||
//Schnittstelle und Instanzdaten (UDT "UZylinder") unveraendert.
|
||||
VAR_INPUT
|
||||
Automatik : Bool; // Automatikbetrieb ist aktiv
|
||||
Handbetrieb : Bool; // Handbetrieb ist aktiv
|
||||
T_Ast : Int := 5; // Zeit bis AST erreicht ist (*100ms)
|
||||
T_Gst : Int := 5; // Zeit bis GST erreicht ist (*100ms)
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
ZylAst : Bool; // Vorwaerts-Ausgang des Antriebs
|
||||
ZylGst : Bool; // Rueckwaerts-Ausgang des Antriebs
|
||||
END_VAR
|
||||
|
||||
VAR
|
||||
Stat { S7_SetPoint := 'True'} : "UZylinder";
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
tZeitNullen : Bool; // AWL-Sprungziel "tdel": Zeitzelle nullen + Old_Ast nachfuehren
|
||||
tFehler : Bool; // NW5: undefinierter Zustand / Befehl
|
||||
tFlanke : Bool; // NW10: Flankenergebnis
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
REGION NW1: Zeitwert loeschen
|
||||
// Endstellung erreicht oder Fahrtrichtung hat gewechselt -> Zeit nullen
|
||||
#tZeitNullen := (#ZylAst AND (#Stat.B_AST OR NOT #Stat.Old_Ast))
|
||||
OR (NOT #ZylAst AND (#Stat.B_GST OR #Stat.Old_Ast));
|
||||
END_REGION
|
||||
|
||||
IF NOT #tZeitNullen THEN
|
||||
REGION NW2: AST-Erreichtzeit abgelaufen ?
|
||||
IF #Stat.T_AST_Akt = 9999 THEN
|
||||
#Stat.T_AST_Akt := #T_Ast; // Sollwert uebergeben (9999 = kein Zeitwert angegeben)
|
||||
END_IF;
|
||||
|
||||
IF #ZylAst THEN // Zylinder faehrt nach AST ?
|
||||
#Stat.B_GST := FALSE;
|
||||
IF #Stat.T_Zelle >= #Stat.T_AST_Akt THEN // Gewollter Zeitwert erreicht ?
|
||||
#Stat.B_AST := TRUE; // Zylinder hat AST erreicht
|
||||
#tZeitNullen := TRUE; // AWL: JC tdel
|
||||
END_IF;
|
||||
ELSIF NOT #Stat.Typ5_3 THEN // Zylinder haelt seine Stellung !
|
||||
#Stat.B_AST := FALSE;
|
||||
END_IF;
|
||||
END_REGION
|
||||
END_IF;
|
||||
|
||||
IF NOT #tZeitNullen THEN
|
||||
REGION NW3: GST-Erreichtzeit abgelaufen ?
|
||||
IF #Stat.T_GST_Akt = 9999 THEN
|
||||
#Stat.T_GST_Akt := #T_Gst; // Sollwert uebergeben (9999 = kein Zeitwert angegeben)
|
||||
END_IF;
|
||||
|
||||
// Zylinder faehrt nach GST ? (2-fache Ansteuerung: ZylGst / 1-fache: ZylAst aus)
|
||||
IF ((#Stat.Typ5_2 OR #Stat.Typ5_3) AND #ZylGst)
|
||||
OR (#Stat.Typ3_2 AND NOT #ZylAst) THEN
|
||||
#Stat.B_AST := FALSE;
|
||||
IF #Stat.T_Zelle >= #Stat.T_GST_Akt THEN // Gewollter Zeitwert erreicht ?
|
||||
#Stat.B_GST := TRUE; // Zylinder hat GST erreicht
|
||||
#tZeitNullen := TRUE; // AWL: JC tdel
|
||||
END_IF;
|
||||
ELSIF NOT #Stat.Typ5_3 THEN // Zylinder haelt seine Stellung !
|
||||
#Stat.B_GST := FALSE;
|
||||
END_IF;
|
||||
END_REGION
|
||||
END_IF;
|
||||
|
||||
IF NOT #tZeitNullen THEN
|
||||
REGION NW4: Zeit laeuft
|
||||
// Nicht zaehlen, wenn 2-fach angesteuerter Zylinder gar nicht angesteuert wird;
|
||||
// Zaehlung nur mit dem 100ms-Impuls
|
||||
IF "100ms-Impuls"
|
||||
AND NOT ((#Stat.Typ5_2 OR #Stat.Typ5_3) AND NOT #ZylGst AND NOT #ZylAst) THEN
|
||||
#Stat.T_Zelle := #Stat.T_Zelle + WORD_TO_INT("MW2"); // 100ms-Takte
|
||||
END_IF;
|
||||
END_REGION
|
||||
ELSE
|
||||
// AWL-Sprungziel "tdel" (aus NW1/NW2/NW3)
|
||||
#Stat.T_Zelle := 0; // Zeitwert loeschen
|
||||
#Stat.Old_Ast := #ZylAst; // Fuhr letzten Zyklus nach AST
|
||||
END_IF;
|
||||
|
||||
REGION NW5: Undefinierte Zustaende / Befehle
|
||||
#tFehler := (#Automatik AND #Handbetrieb) // Automatik + Hand aktiv
|
||||
OR (NOT #Automatik AND NOT #Handbetrieb) // keine Betriebsart
|
||||
OR (#Stat.Ast AND #Stat.Gst) // AST- + GST-fahren gleichzeitig aktiv
|
||||
OR (NOT #Stat.Typ3_2 AND NOT #Stat.Typ5_2 AND NOT #Stat.Typ5_3) // undefinierter Ventil-Typ
|
||||
OR (#Stat.Typ3_2 AND (#Stat.Typ5_2 OR #Stat.Typ5_3)) // mehrere Ventil-Typen
|
||||
OR (#Stat.Typ5_2 AND #Stat.Typ5_3);
|
||||
|
||||
IF #tFehler THEN // Zylinderstellung bleibt eingefroren
|
||||
IF #Stat.Typ5_3 THEN // Zylinder haelt seine Stellung !
|
||||
#Stat.Ast := FALSE; // Bewegungsbits loeschen
|
||||
#Stat.Gst := FALSE;
|
||||
END_IF;
|
||||
RETURN; // AWL: BEU
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW6: Zylinder auf Arbeitsstellung fahren
|
||||
#ZylAst := #Stat.Ast;
|
||||
END_REGION
|
||||
|
||||
REGION NW7: Zylinder auf Grundstellung fahren
|
||||
#ZylGst := (#Stat.Typ5_2 OR #Stat.Typ5_3) AND #Stat.Gst;
|
||||
END_REGION
|
||||
|
||||
REGION NW8: Daten, die zurueckgesetzt werden
|
||||
IF #Stat.Typ5_3 THEN // Zylinder haelt seine Stellung !
|
||||
#Stat.Ast := FALSE; // Bewegungsbits loeschen
|
||||
#Stat.Gst := FALSE;
|
||||
END_IF;
|
||||
IF #Stat.Typ3_2 THEN
|
||||
#Stat.Gst := FALSE; // Es gibt kein GST fahren !
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW9: Ausgaenge an OP uebergeben
|
||||
#Stat.OP_Out_GST := #ZylGst;
|
||||
#Stat.OP_Out_AST := #ZylAst;
|
||||
END_REGION
|
||||
|
||||
REGION NW10: Statistik
|
||||
// Positive Flanke ZylAst (AWL: FP Stat.FP_AST)
|
||||
#tFlanke := #ZylAst AND NOT #Stat.FP_AST;
|
||||
#Stat.FP_AST := #ZylAst;
|
||||
IF #tFlanke THEN
|
||||
#Stat.Statistik.AST_Schaltungen := #Stat.Statistik.AST_Schaltungen + 1;
|
||||
END_IF;
|
||||
|
||||
// Positive Flanke ZylGst bei Typ5_3 (AWL: FP Stat.FP_AST2)
|
||||
#tFlanke := #ZylGst AND #Stat.Typ5_3 AND NOT #Stat.FP_AST2;
|
||||
#Stat.FP_AST2 := #ZylGst AND #Stat.Typ5_3;
|
||||
IF #tFlanke THEN
|
||||
#Stat.Statistik.AST2_Schaltungen := #Stat.Statistik.AST2_Schaltungen + 1;
|
||||
END_IF;
|
||||
END_REGION
|
||||
END_FUNCTION_BLOCK
|
||||
@@ -0,0 +1,460 @@
|
||||
FUNCTION "DatenOut" : Void
|
||||
TITLE = Datensatz in Datenliste suchen / lesen / loeschen
|
||||
{ S7_Optimized_Access := 'FALSE' }
|
||||
AUTHOR : IBAE
|
||||
FAMILY : Module
|
||||
NAME : DatenOut
|
||||
//SCL-Portierung des AWL-Bausteins FC86 "DatenOut" (verhaltensgleich).
|
||||
//
|
||||
//Datenlisten-Kopf im Datenlisten-DB ab Byte SSDW:
|
||||
// +0 Kopf (Byte, 16#DA = Datenliste, 16#DB = Sort-Datenliste)
|
||||
// +1 Datensatzlaenge (Byte, in Worten, 1..128)
|
||||
// +2 Listenlaenge (Int, max. Anzahl Datensaetze)
|
||||
// +4 Stammdaten (Int, Anzahl Suchworte 1..9)
|
||||
// +6 IndDB (Word, indizierter DB; nur bei Stammdaten=1)
|
||||
// +8 IndDB_Laenge (Int, Laenge indizierter DB in Eintraegen)
|
||||
// +10 IndDaten_FC (Int, Wandel-FC-Nummer fuer indizierte Suche)
|
||||
// +12 Suchzeiger / letzter Zugriff (Int, Byte-Adresse)
|
||||
// +16 AnzahlEintraege (Int)
|
||||
// +20 Datenbereich, dahinter Endekennung 16#DAEE bzw. 16#DBEE
|
||||
//
|
||||
//Aufrufparameter PARA (High-Byte = Para1, Low-Byte = Para2):
|
||||
// Para1: 00=suchen, E1=suchen+loeschen, F1=1.Datensatz holen,
|
||||
// FF=naechsten Datensatz holen, 02..80=Offset-Suche ab Wort X
|
||||
// Para2: 00=Stammdaten aus Kopf, sonst Anzahl Stammdaten (1..9)
|
||||
//
|
||||
//RE_VAL: Bit0=kein Datenlistenbereich, Bit1=Kopier-/SFC-Fehler, Bit2=Listenende,
|
||||
// Bit3=falscher Parameter, Bit4=undef. Suchdaten, Bit5=undef. OutDaten,
|
||||
// Bit6=Datensatz nicht vorhanden, Bit7=Daten vorhanden. Erfolg zusaetzlich via ENO/BR.
|
||||
//
|
||||
//ABWEICHUNGEN ZUM AWL-ORIGINAL:
|
||||
// - SEARCHDATEN/OUTDATEN sind VARIANT statt POINTER (S7-1500/SCL).
|
||||
// SEARCHDATEN muss mind. Stammdaten*2 Bytes referenzieren.
|
||||
// OUTDATEN sollte exakt Datensatzlaenge*2 Bytes gross sein (groessere Ziele
|
||||
// werden hinter dem Datensatz mit 0 aufgefuellt).
|
||||
// - Die DB-Laengenpruefung des Such-/Out-DB (AWL NW5/NW8) entfaellt, da der
|
||||
// Variant-Zugriff typsicher ist. Fehlerbit 5 (undef. OutDaten) wird nicht mehr erzeugt.
|
||||
// - Der indirekte Aufruf UC FC[#Wandel_FC] ist als CASE ausgefuehrt
|
||||
// (aktuell einziger Wandel-FC im Projekt: FC1000 "Wandel_FC_fuer_DB1000").
|
||||
// - Position wird mit 0 initialisiert (AWL: undefiniert, wenn nicht gefunden).
|
||||
VAR_INPUT
|
||||
SSDB : Int; // Datenlisten-DB
|
||||
SSDW : Int; // Datenlisten-1.DW (Byte-Offset des Kopfs)
|
||||
PARA : Word; // Aufrufparameter (Auftrag / Anzahl Stammdaten)
|
||||
SEARCHDATEN : Variant; // Such-Daten-Schnittstelle (Stammdaten Worte)
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
RE_VAL : Byte; // Rueckmeldung (Fehler-/Statusbits)
|
||||
Position : Int; // Daten stehen an angegebener Position in Datenliste
|
||||
END_VAR
|
||||
|
||||
VAR_IN_OUT
|
||||
OUTDATEN : Variant; // OutDaten-Schnittstelle (Datensatzlaenge Worte)
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
Kopf : Byte; // Datenliste-Kennung (DA/DB)
|
||||
Datensatzlaenge : Int; // Datensatzlaenge in Word (1..128)
|
||||
Listenlaenge : Int; // Laenge der Datenliste (max. Anzahl Datensaetze)
|
||||
Stammdaten : Int; // Stammdatenlaenge in Word (1..9)
|
||||
IndDB : Word; // Indizierter DB (nur bei Stammdaten = 1 erlaubt !!!)
|
||||
IndDB_Laenge : Int; // Laenge des indizierten DB in Eintraegen
|
||||
IndDaten_FC : Int; // FC zum Wandeln der indizierten Daten
|
||||
Suchzeiger : Int; // Speicherung des letzten Zugriffs / Suchzeiger (Byte-Adresse)
|
||||
Para1 : Byte; // 1.Aufrufparameter
|
||||
Para2 : Byte; // 2.Aufrufparameter
|
||||
OffsetDaten_1 : Int; // Stammdaten beginnen ab hier (Byte-Offset)
|
||||
SFC_RET_VAL : Int; // Rueckmeldung SFC-/Serialize-Aufruf
|
||||
SFC_DB_LENGTH : Word; // Rueckmeldung TEST_DB-Aufruf
|
||||
SFC_WRITE_PROT : Bool; // Rueckmeldung TEST_DB-Aufruf (letzter Aufruf!)
|
||||
DB_Laenge : Int; // DB-Laenge nach BCD-Wandlung (wie AWL)
|
||||
Datenlisten_Ende : Int; // Ende der Datenliste (Byte-Offset Endekennung)
|
||||
Datenblock_inBytes : Int; // Datenblocklaenge in Bytes
|
||||
Daten_1 : Word; // 1.Datenwort der Suchdaten
|
||||
Daten_2 : DWord; // 1.+2.Datenwort der Suchdaten (fuer Wandel-FC)
|
||||
Wandel_FC : Int; // Wandel-FC-Nummer
|
||||
Suchstelle : Int; // Bei angegebenem Datensatz suchen (Position)
|
||||
LoeschPos : Int; // Loesch-Position
|
||||
Indok : Bool; // Indizierung moeglich
|
||||
IndZeiger_ok : Bool; // Indizier-Zeiger angegeben
|
||||
Weitersuchen : Bool; // Weitersuchen ist aktiv (Para1 F1/FF)
|
||||
Offsetsuchen : Bool; // Offsetsuchen ist aktiv
|
||||
Sortliste : Bool; // Sortliste wird durchsucht
|
||||
tRE_VAL : Byte; // Rueckmeldung (Zwischenwert)
|
||||
tVKE : Bool; // Ergebnis fuer ENO (BR im AWL)
|
||||
tGefunden : Bool; // Vergleichs-/Suchergebnis
|
||||
tDbList : DInt; // Datenlisten-DB-Nummer fuer PEEK/POKE
|
||||
tDbInd : DInt; // Indizierter DB fuer PEEK/POKE
|
||||
tIndex : Int; // Indexwert fuer indizierte Suche
|
||||
IndByteOfs : Int; // Byte-Offset des Index-Eintrags im IndDB
|
||||
tWort : Word; // Wort-Zwischenwert
|
||||
tAnz : Int; // Anzahl-Zwischenwert
|
||||
tAnzBytes : Int; // Anzahl zu verschiebender Bytes
|
||||
tPos : DInt; // Serialize-/Deserialize-Position
|
||||
i : Int; // Schleifenzaehler
|
||||
tSuchPuffer : Array[0..257] of Byte; // Zwischenpuffer SEARCHDATEN
|
||||
tRecPuffer : Array[0..257] of Byte; // Zwischenpuffer Datensatz -> OUTDATEN
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
#tRE_VAL := 16#00;
|
||||
#Suchstelle := 0;
|
||||
#Position := 0;
|
||||
|
||||
REGION NW1: Schnittstellen-DB sinnvoll ?
|
||||
#tDbList := 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);
|
||||
"BCD (4stellig)_to_INT"(BCD_Wert := #SFC_DB_LENGTH, Int_Wert => #DB_Laenge);
|
||||
|
||||
IF (#SFC_RET_VAL <> 0) OR #SFC_WRITE_PROT THEN // schreibgeschuetzt
|
||||
GOTO mErr0; // kein Datenlisten-Bereich
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW2: Datenlisten-Kopf holen (AWL: Quelle-Pointer + BLKMOV, 10 Worte)
|
||||
IF #SSDW < 0 OR (#SSDW + 20) > #DB_Laenge THEN
|
||||
GOTO mErr1; // Kopf liegt nicht im DB (AWL: SFC20-Fehler)
|
||||
END_IF;
|
||||
|
||||
#Kopf := PEEK(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW));
|
||||
#Datensatzlaenge := BYTE_TO_INT(PEEK(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 1)));
|
||||
#Listenlaenge := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 2)));
|
||||
#Stammdaten := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 4)));
|
||||
#IndDB := PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 6));
|
||||
#IndDB_Laenge := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 8)));
|
||||
#IndDaten_FC := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 10)));
|
||||
#Suchzeiger := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 12)));
|
||||
END_REGION
|
||||
|
||||
REGION NW3: Zeigerueberpruefung
|
||||
#Sortliste := (#Kopf = 16#DB); // Sort-Datenlistenkennung ?
|
||||
IF NOT #Sortliste AND (#Kopf <> 16#DA) THEN // Datenlistenkennung ?
|
||||
GOTO mErr0; // kein Datenlistenbereich
|
||||
END_IF;
|
||||
|
||||
IF (#Datensatzlaenge < 1) OR (#Datensatzlaenge > 128)
|
||||
OR (#Listenlaenge < 1)
|
||||
OR (#Stammdaten < 1) OR (#Stammdaten > 9)
|
||||
OR (#Stammdaten > #Datensatzlaenge) THEN
|
||||
GOTO mErr0; // kein Datenlistenbereich
|
||||
END_IF;
|
||||
|
||||
#Datenlisten_Ende := #Listenlaenge * #Datensatzlaenge * 2 + 20 + #SSDW;
|
||||
|
||||
IF #Datenlisten_Ende > #DB_Laenge THEN // Datenlisten-DB ist zu kurz!
|
||||
GOTO mErr0; // kein Datenlisten-Bereich
|
||||
END_IF;
|
||||
|
||||
#tWort := PEEK_WORD(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#Datenlisten_Ende));
|
||||
IF (#tWort <> 16#DAEE) AND (#tWort <> 16#DBEE) THEN // Endekennung ?
|
||||
GOTO mErr0; // kein Datenlisten-Bereich
|
||||
END_IF;
|
||||
|
||||
#Datenblock_inBytes := #Datensatzlaenge * 2;
|
||||
END_REGION
|
||||
|
||||
REGION NW4: Auftrag kontrollieren
|
||||
#Indok := FALSE;
|
||||
#IndZeiger_ok := FALSE;
|
||||
#Weitersuchen := FALSE;
|
||||
#Offsetsuchen := FALSE;
|
||||
#OffsetDaten_1 := 0; // Vorerst kein Offset
|
||||
|
||||
#Para1 := WORD_TO_BYTE(SHR(IN := #PARA, N := 8));
|
||||
#Para2 := WORD_TO_BYTE(#PARA);
|
||||
|
||||
IF #Para1 = 16#00 THEN // Normale Suche
|
||||
GOTO mW401;
|
||||
END_IF;
|
||||
|
||||
IF #Para1 = 16#E1 THEN // Suchen + loeschen
|
||||
IF #Sortliste THEN
|
||||
GOTO mErr3; // Austragen aus Sortliste nicht erlaubt !
|
||||
END_IF;
|
||||
GOTO mW401;
|
||||
END_IF;
|
||||
|
||||
IF #Para1 = 16#F1 THEN // 1.Datensatz holen
|
||||
IF (#Para2 <> 16#00) OR #Sortliste THEN
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
#Weitersuchen := TRUE;
|
||||
#Suchzeiger := #SSDW + 20;
|
||||
GOTO mNw08; // OutDaten ok ?
|
||||
END_IF;
|
||||
|
||||
IF #Para1 = 16#FF THEN // Naechsten Datensatz holen
|
||||
IF (#Para2 <> 16#00) OR #Sortliste THEN
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
#Weitersuchen := TRUE;
|
||||
#Suchzeiger := #Suchzeiger + #Datenblock_inBytes; // Naechsten Datensatz suchen
|
||||
IF (#Datenblock_inBytes * #Listenlaenge + #SSDW + 20) > #Suchzeiger THEN
|
||||
GOTO mNw08; // Noch nicht am Ende
|
||||
END_IF;
|
||||
GOTO mErr2; // Listenende erreicht
|
||||
END_IF;
|
||||
|
||||
// Offset-Suche: Para1 = Offset des 1.Stammdatenworts (2..128)
|
||||
IF (BYTE_TO_INT(#Para1) < 2)
|
||||
OR (BYTE_TO_INT(#Para1) > 128)
|
||||
OR (BYTE_TO_INT(#Para1) > #Datensatzlaenge) THEN
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
#OffsetDaten_1 := (BYTE_TO_INT(#Para1) - 1) * 2; // Richtiger Offset der 1.Stammdaten
|
||||
#Suchzeiger := 0; // Suche ab Anfang
|
||||
#Offsetsuchen := TRUE;
|
||||
|
||||
mW401: IF #Para2 <> 16#00 THEN
|
||||
#Stammdaten := BYTE_TO_INT(#Para2); // Anzahl der Stammdaten aus Aufruf
|
||||
IF (#Stammdaten > #Datensatzlaenge)
|
||||
OR (#Stammdaten < 1)
|
||||
OR (#Stammdaten > 9) THEN
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
END_IF;
|
||||
|
||||
IF #Offsetsuchen THEN
|
||||
IF (BYTE_TO_INT(#Para1) - 1 + #Stammdaten) > #Datensatzlaenge THEN // Letzte Stammdaten zu gross ?
|
||||
GOTO mErr3; // Falscher Aufrufparameter
|
||||
END_IF;
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW5: Such-Daten uebernehmen (AWL: Pointer-Parsing)
|
||||
#Suchzeiger := 0;
|
||||
|
||||
#tPos := 0;
|
||||
#SFC_RET_VAL := Serialize(SRC_VARIANT := #SEARCHDATEN, DEST_ARRAY := #tSuchPuffer, POS := #tPos);
|
||||
IF (#SFC_RET_VAL <> 0) OR (#tPos < INT_TO_DINT(#Stammdaten * 2)) THEN
|
||||
GOTO mErr4; // Undefinierte Suchdaten
|
||||
END_IF;
|
||||
|
||||
#Daten_1 := SHL(IN := BYTE_TO_WORD(#tSuchPuffer[0]), N := 8) OR BYTE_TO_WORD(#tSuchPuffer[1]);
|
||||
#Daten_2 := SHL(IN := BYTE_TO_DWORD(#tSuchPuffer[0]), N := 24)
|
||||
OR SHL(IN := BYTE_TO_DWORD(#tSuchPuffer[1]), N := 16)
|
||||
OR SHL(IN := BYTE_TO_DWORD(#tSuchPuffer[2]), N := 8)
|
||||
OR BYTE_TO_DWORD(#tSuchPuffer[3]);
|
||||
|
||||
// Mindestens ein Suchwort <> 0 ?
|
||||
#tGefunden := FALSE;
|
||||
FOR #i := 0 TO #Stammdaten - 1 DO
|
||||
IF (#tSuchPuffer[#i * 2] <> 16#00) OR (#tSuchPuffer[#i * 2 + 1] <> 16#00) THEN
|
||||
#tGefunden := TRUE;
|
||||
EXIT;
|
||||
END_IF;
|
||||
END_FOR;
|
||||
IF NOT #tGefunden THEN
|
||||
GOTO mErr4; // Undefinierte Stammdaten
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW6: Indiziert suchen ?
|
||||
IF (#IndDB <> 16#0000) AND NOT #Offsetsuchen AND NOT #Sortliste THEN
|
||||
"TEST_DB_TO_ATTR_DB"(DB_Number := #IndDB,
|
||||
RetVal => #SFC_RET_VAL,
|
||||
DB_Length => #SFC_DB_LENGTH,
|
||||
Write_Prot => #SFC_WRITE_PROT);
|
||||
"BCD (4stellig)_to_INT"(BCD_Wert := #SFC_DB_LENGTH, Int_Wert => #DB_Laenge);
|
||||
|
||||
IF (#SFC_RET_VAL = 0) AND NOT #SFC_WRITE_PROT // Indizier-DB vorhanden
|
||||
AND NOT (#IndDB_Laenge * 2 > #DB_Laenge) // Indizier-DB lang genug
|
||||
AND (#Stammdaten < 3) THEN // nicht zu viele Stammdaten
|
||||
#Wandel_FC := #IndDaten_FC;
|
||||
IF #Wandel_FC > 0 THEN
|
||||
"MW90" := #Daten_1; // Stammdaten (1.DW) uebergeben
|
||||
"MD94" := #Daten_2; // Stammdaten (1.+2.DW) uebergeben
|
||||
CASE #Wandel_FC OF // AWL: UC FC[#Wandel_FC]
|
||||
1000:
|
||||
"Wandel_FC_für_DB1000"();
|
||||
ELSE
|
||||
; // unbekannter Wandel-FC: MW92 bleibt unveraendert
|
||||
END_CASE;
|
||||
#tIndex := WORD_TO_INT("MW92"); // Rueckwert vom FC
|
||||
ELSE
|
||||
#tIndex := WORD_TO_INT(#Daten_1); // Stammdaten direkt als Index
|
||||
END_IF;
|
||||
|
||||
IF (#tIndex >= 0) AND (#tIndex < #IndDB_Laenge) THEN // Zeiger im Ind.DB ?
|
||||
#IndByteOfs := #tIndex * 2; // DB byteorientiert
|
||||
#tDbInd := INT_TO_DINT(WORD_TO_INT(#IndDB));
|
||||
#Suchzeiger := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbInd,
|
||||
byteOffset := INT_TO_DINT(#IndByteOfs)));
|
||||
#Indok := TRUE; // Indizierung moeglich
|
||||
#IndZeiger_ok := TRUE; // Indizier-Zeiger angegeben
|
||||
END_IF;
|
||||
END_IF;
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW7: Kontrolle auf gleiche Stammdaten (Suchschleife)
|
||||
IF (#Suchzeiger = 0)
|
||||
OR (((#Suchzeiger - 20) MOD #Datenblock_inBytes) <> 0) THEN // Suchzeiger nicht im Raster
|
||||
// Suche bei 1.Datensatz beginnen (AWL: W700)
|
||||
#Suchstelle := 1;
|
||||
#IndZeiger_ok := FALSE;
|
||||
#Suchzeiger := #SSDW + 20;
|
||||
ELSE
|
||||
#Suchstelle := 0; // Bei angegebener Stelle suchen
|
||||
END_IF;
|
||||
|
||||
REPEAT
|
||||
// Stammdaten vergleichen (AWL: W701/L700-Schleife)
|
||||
#tGefunden := TRUE;
|
||||
FOR #i := 0 TO #Stammdaten - 1 DO
|
||||
#tWort := SHL(IN := BYTE_TO_WORD(#tSuchPuffer[#i * 2]), N := 8)
|
||||
OR BYTE_TO_WORD(#tSuchPuffer[#i * 2 + 1]);
|
||||
IF #tWort <> PEEK_WORD(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #OffsetDaten_1 + #i * 2)) THEN
|
||||
#tGefunden := FALSE;
|
||||
EXIT;
|
||||
END_IF;
|
||||
END_FOR;
|
||||
|
||||
IF #tGefunden THEN
|
||||
#tRE_VAL := 16#80; // Daten sind vorhanden
|
||||
GOTO mNw08; // Datensatz ist in Liste enthalten
|
||||
END_IF;
|
||||
|
||||
// AWL: W703
|
||||
IF #Suchstelle = 0 THEN
|
||||
// Suche bei angegebener Stelle erfolglos: ab 1.Datensatz neu (AWL: W700)
|
||||
#Suchstelle := 1;
|
||||
#IndZeiger_ok := FALSE;
|
||||
#Suchzeiger := #SSDW + 20;
|
||||
ELSE
|
||||
#Suchstelle := #Suchstelle + 1;
|
||||
IF #Suchstelle > #Listenlaenge THEN // Alle Datensaetze durchsucht
|
||||
GOTO mErr6; // Datensatz ist nicht vorhanden
|
||||
END_IF;
|
||||
#Suchzeiger := #Suchzeiger + #Datenblock_inBytes; // AWL: W705
|
||||
END_IF;
|
||||
UNTIL FALSE END_REPEAT;
|
||||
END_REGION
|
||||
|
||||
REGION NW8+NW9: Datensatz an OUTDATEN uebergeben
|
||||
mNw08: FILL_BLK(IN := BYTE#16#00, COUNT := 258, OUT => #tRecPuffer[0]);
|
||||
|
||||
FOR #i := 0 TO #Datensatzlaenge * 2 - 1 DO
|
||||
#tRecPuffer[#i] := PEEK(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #i));
|
||||
END_FOR;
|
||||
|
||||
#tPos := 0;
|
||||
#SFC_RET_VAL := Deserialize(SRC_ARRAY := #tRecPuffer, DEST_VARIANT := #OUTDATEN, POS := #tPos);
|
||||
IF #SFC_RET_VAL <> 0 THEN
|
||||
GOTO mErr1; // Kopier-Fehler (AWL: SFC20-Aufruf mit Fehler)
|
||||
END_IF;
|
||||
|
||||
IF NOT #SFC_WRITE_PROT THEN // Datenliste kann beschrieben werden
|
||||
// Speicherung letzter Zugriff
|
||||
POKE(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 12),
|
||||
value := INT_TO_WORD(#Suchzeiger));
|
||||
#Position := #Suchstelle; // Uebergabe nach aussen
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW10+NW11: Daten aus Datenliste loeschen ?
|
||||
IF (#Para1 = 16#E1) AND NOT #SFC_WRITE_PROT THEN // Daten loeschen ?
|
||||
IF #Indok THEN
|
||||
// NW11: Bei ind. Zugriff nur loeschen, nicht verschieben, keine Anzahl!
|
||||
FOR #i := 0 TO #Datensatzlaenge - 1 DO
|
||||
POKE(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #i * 2), value := WORD#16#0000);
|
||||
END_FOR;
|
||||
POKE(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 12),
|
||||
value := WORD#16#0000); // letzter Zugriff loeschen
|
||||
#Suchzeiger := 0;
|
||||
#IndZeiger_ok := FALSE;
|
||||
ELSE
|
||||
// NW10: loeschen + nachfolgende Datensaetze vorschieben
|
||||
#LoeschPos := (#Suchzeiger - 20 - #SSDW) / #Datenblock_inBytes + 1; // Stelle, die geloescht wird
|
||||
IF #LoeschPos <> #Listenlaenge THEN // nicht der letzte Eintrag
|
||||
// Daten verschieben (AWL: BLKMOV; hier bytewise vorwaerts = sicher bei Ueberlappung)
|
||||
#tAnzBytes := (#Listenlaenge - #LoeschPos) * #Datenblock_inBytes;
|
||||
FOR #i := 0 TO #tAnzBytes - 1 DO
|
||||
POKE(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #i),
|
||||
value := PEEK(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Suchzeiger + #Datenblock_inBytes + #i)));
|
||||
END_FOR;
|
||||
END_IF;
|
||||
|
||||
// Letzten Eintrag loeschen (AWL: Last)
|
||||
FOR #i := 0 TO #Datensatzlaenge - 1 DO
|
||||
POKE(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#Datenlisten_Ende - #Datenblock_inBytes + #i * 2),
|
||||
value := WORD#16#0000);
|
||||
END_FOR;
|
||||
|
||||
// Speicherung letzter Zugriff loeschen
|
||||
POKE(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 12),
|
||||
value := WORD#16#0000);
|
||||
#Suchzeiger := 0;
|
||||
#IndZeiger_ok := FALSE;
|
||||
|
||||
// Anzahl Eintraege -1 (Minimum 0)
|
||||
#tAnz := WORD_TO_INT(PEEK_WORD(area := 16#84, dbNumber := #tDbList,
|
||||
byteOffset := INT_TO_DINT(#SSDW + 16))) - 1;
|
||||
IF #tAnz < 0 THEN
|
||||
#tAnz := 0; // Minimum uebergeben
|
||||
END_IF;
|
||||
POKE(area := 16#84, dbNumber := #tDbList, byteOffset := INT_TO_DINT(#SSDW + 16),
|
||||
value := INT_TO_WORD(#tAnz));
|
||||
END_IF;
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW12: Weitersuchen aktiv ? Datensatz vorhanden oder 0 ?
|
||||
IF #Weitersuchen THEN
|
||||
#tGefunden := FALSE;
|
||||
FOR #i := 0 TO #Datensatzlaenge - 1 DO
|
||||
IF (#tRecPuffer[#i * 2] <> 16#00) OR (#tRecPuffer[#i * 2 + 1] <> 16#00) THEN
|
||||
#tGefunden := TRUE; // Etwas angegeben
|
||||
EXIT;
|
||||
END_IF;
|
||||
END_FOR;
|
||||
IF #tGefunden THEN
|
||||
#tRE_VAL := 16#80; // Daten sind vorhanden
|
||||
ELSE
|
||||
#tRE_VAL := 16#40; // Daten sind nicht vorhanden
|
||||
END_IF;
|
||||
GOTO mEnde;
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW13: Indizierung ok ?
|
||||
IF #Indok AND NOT #IndZeiger_ok THEN
|
||||
// Index-Eintrag im indizierten DB aktualisieren
|
||||
POKE(area := 16#84, dbNumber := #tDbInd, byteOffset := INT_TO_DINT(#IndByteOfs),
|
||||
value := INT_TO_WORD(#Suchzeiger));
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW14+NW15: Fehlerausgabe / RET_VAL
|
||||
mEnde: #tVKE := TRUE; // VKE = 1 ohne Fehler !!!
|
||||
GOTO mOk;
|
||||
|
||||
mErr0: #tRE_VAL := 16#01; // Kein Datenlistenbereich
|
||||
GOTO mErrX;
|
||||
mErr1: #tRE_VAL := 16#02; // Kopier-/SFC-Fehler
|
||||
GOTO mErrX;
|
||||
mErr2: #tRE_VAL := 16#04; // Listenende ist erreicht
|
||||
GOTO mErrX;
|
||||
mErr3: #tRE_VAL := 16#08; // Falscher Parameter bei Aufruf
|
||||
GOTO mErrX;
|
||||
mErr4: #tRE_VAL := 16#10; // Undefinierte Suchdaten
|
||||
GOTO mErrX;
|
||||
mErr6: #tRE_VAL := 16#40; // Datensatz nicht vorhanden
|
||||
|
||||
mErrX: #tVKE := FALSE; // VKE = 0 bei Fehler !!!
|
||||
|
||||
mOk: #RE_VAL := #tRE_VAL;
|
||||
ENO := #tVKE; // AWL: SAVE (BR-Bit fuer JBI beim Aufrufer)
|
||||
END_REGION
|
||||
END_FUNCTION
|
||||
@@ -0,0 +1,303 @@
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,146 @@
|
||||
FUNCTION_BLOCK "MZylinder"
|
||||
TITLE = Zylinderansteuerung
|
||||
{ S7_Optimized_Access := 'FALSE' }
|
||||
AUTHOR : IBAE
|
||||
FAMILY : Module
|
||||
NAME : MZylinder
|
||||
//SCL-Portierung des AWL-Bausteins FB92 "MZylinder" (verhaltensgleich).
|
||||
//Schnittstelle und Instanzdaten (UDT "UZylinder") unveraendert.
|
||||
VAR_INPUT
|
||||
Automatik : Bool; // Automatikbetrieb ist aktiv
|
||||
Handbetrieb : Bool; // Handbetrieb ist aktiv
|
||||
T_Ast : Int := 5; // Zeit bis AST erreicht ist (*100ms)
|
||||
T_Gst : Int := 5; // Zeit bis GST erreicht ist (*100ms)
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
ZylAst : Bool; // Vorwaerts-Ausgang des Antriebs
|
||||
ZylGst : Bool; // Rueckwaerts-Ausgang des Antriebs
|
||||
END_VAR
|
||||
|
||||
VAR
|
||||
Stat { S7_SetPoint := 'True'} : "UZylinder";
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
tZeitNullen : Bool; // AWL-Sprungziel "tdel": Zeitzelle nullen + Old_Ast nachfuehren
|
||||
tFehler : Bool; // NW5: undefinierter Zustand / Befehl
|
||||
tFlanke : Bool; // NW10: Flankenergebnis
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
REGION NW1: Zeitwert loeschen
|
||||
// Endstellung erreicht oder Fahrtrichtung hat gewechselt -> Zeit nullen
|
||||
#tZeitNullen := (#ZylAst AND (#Stat.B_AST OR NOT #Stat.Old_Ast))
|
||||
OR (NOT #ZylAst AND (#Stat.B_GST OR #Stat.Old_Ast));
|
||||
END_REGION
|
||||
|
||||
IF NOT #tZeitNullen THEN
|
||||
REGION NW2: AST-Erreichtzeit abgelaufen ?
|
||||
IF #Stat.T_AST_Akt = 9999 THEN
|
||||
#Stat.T_AST_Akt := #T_Ast; // Sollwert uebergeben (9999 = kein Zeitwert angegeben)
|
||||
END_IF;
|
||||
|
||||
IF #ZylAst THEN // Zylinder faehrt nach AST ?
|
||||
#Stat.B_GST := FALSE;
|
||||
IF #Stat.T_Zelle >= #Stat.T_AST_Akt THEN // Gewollter Zeitwert erreicht ?
|
||||
#Stat.B_AST := TRUE; // Zylinder hat AST erreicht
|
||||
#tZeitNullen := TRUE; // AWL: JC tdel
|
||||
END_IF;
|
||||
ELSIF NOT #Stat.Typ5_3 THEN // Zylinder haelt seine Stellung !
|
||||
#Stat.B_AST := FALSE;
|
||||
END_IF;
|
||||
END_REGION
|
||||
END_IF;
|
||||
|
||||
IF NOT #tZeitNullen THEN
|
||||
REGION NW3: GST-Erreichtzeit abgelaufen ?
|
||||
IF #Stat.T_GST_Akt = 9999 THEN
|
||||
#Stat.T_GST_Akt := #T_Gst; // Sollwert uebergeben (9999 = kein Zeitwert angegeben)
|
||||
END_IF;
|
||||
|
||||
// Zylinder faehrt nach GST ? (2-fache Ansteuerung: ZylGst / 1-fache: ZylAst aus)
|
||||
IF ((#Stat.Typ5_2 OR #Stat.Typ5_3) AND #ZylGst)
|
||||
OR (#Stat.Typ3_2 AND NOT #ZylAst) THEN
|
||||
#Stat.B_AST := FALSE;
|
||||
IF #Stat.T_Zelle >= #Stat.T_GST_Akt THEN // Gewollter Zeitwert erreicht ?
|
||||
#Stat.B_GST := TRUE; // Zylinder hat GST erreicht
|
||||
#tZeitNullen := TRUE; // AWL: JC tdel
|
||||
END_IF;
|
||||
ELSIF NOT #Stat.Typ5_3 THEN // Zylinder haelt seine Stellung !
|
||||
#Stat.B_GST := FALSE;
|
||||
END_IF;
|
||||
END_REGION
|
||||
END_IF;
|
||||
|
||||
IF NOT #tZeitNullen THEN
|
||||
REGION NW4: Zeit laeuft
|
||||
// Nicht zaehlen, wenn 2-fach angesteuerter Zylinder gar nicht angesteuert wird;
|
||||
// Zaehlung nur mit dem 100ms-Impuls
|
||||
IF "100ms-Impuls"
|
||||
AND NOT ((#Stat.Typ5_2 OR #Stat.Typ5_3) AND NOT #ZylGst AND NOT #ZylAst) THEN
|
||||
#Stat.T_Zelle := #Stat.T_Zelle + WORD_TO_INT("MW2"); // 100ms-Takte
|
||||
END_IF;
|
||||
END_REGION
|
||||
ELSE
|
||||
// AWL-Sprungziel "tdel" (aus NW1/NW2/NW3)
|
||||
#Stat.T_Zelle := 0; // Zeitwert loeschen
|
||||
#Stat.Old_Ast := #ZylAst; // Fuhr letzten Zyklus nach AST
|
||||
END_IF;
|
||||
|
||||
REGION NW5: Undefinierte Zustaende / Befehle
|
||||
#tFehler := (#Automatik AND #Handbetrieb) // Automatik + Hand aktiv
|
||||
OR (NOT #Automatik AND NOT #Handbetrieb) // keine Betriebsart
|
||||
OR (#Stat.Ast AND #Stat.Gst) // AST- + GST-fahren gleichzeitig aktiv
|
||||
OR (NOT #Stat.Typ3_2 AND NOT #Stat.Typ5_2 AND NOT #Stat.Typ5_3) // undefinierter Ventil-Typ
|
||||
OR (#Stat.Typ3_2 AND (#Stat.Typ5_2 OR #Stat.Typ5_3)) // mehrere Ventil-Typen
|
||||
OR (#Stat.Typ5_2 AND #Stat.Typ5_3);
|
||||
|
||||
IF #tFehler THEN // Zylinderstellung bleibt eingefroren
|
||||
IF #Stat.Typ5_3 THEN // Zylinder haelt seine Stellung !
|
||||
#Stat.Ast := FALSE; // Bewegungsbits loeschen
|
||||
#Stat.Gst := FALSE;
|
||||
END_IF;
|
||||
RETURN; // AWL: BEU
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW6: Zylinder auf Arbeitsstellung fahren
|
||||
#ZylAst := #Stat.Ast;
|
||||
END_REGION
|
||||
|
||||
REGION NW7: Zylinder auf Grundstellung fahren
|
||||
#ZylGst := (#Stat.Typ5_2 OR #Stat.Typ5_3) AND #Stat.Gst;
|
||||
END_REGION
|
||||
|
||||
REGION NW8: Daten, die zurueckgesetzt werden
|
||||
IF #Stat.Typ5_3 THEN // Zylinder haelt seine Stellung !
|
||||
#Stat.Ast := FALSE; // Bewegungsbits loeschen
|
||||
#Stat.Gst := FALSE;
|
||||
END_IF;
|
||||
IF #Stat.Typ3_2 THEN
|
||||
#Stat.Gst := FALSE; // Es gibt kein GST fahren !
|
||||
END_IF;
|
||||
END_REGION
|
||||
|
||||
REGION NW9: Ausgaenge an OP uebergeben
|
||||
#Stat.OP_Out_GST := #ZylGst;
|
||||
#Stat.OP_Out_AST := #ZylAst;
|
||||
END_REGION
|
||||
|
||||
REGION NW10: Statistik
|
||||
// Positive Flanke ZylAst (AWL: FP Stat.FP_AST)
|
||||
#tFlanke := #ZylAst AND NOT #Stat.FP_AST;
|
||||
#Stat.FP_AST := #ZylAst;
|
||||
IF #tFlanke THEN
|
||||
#Stat.Statistik.AST_Schaltungen := #Stat.Statistik.AST_Schaltungen + 1;
|
||||
END_IF;
|
||||
|
||||
// Positive Flanke ZylGst bei Typ5_3 (AWL: FP Stat.FP_AST2)
|
||||
#tFlanke := #ZylGst AND #Stat.Typ5_3 AND NOT #Stat.FP_AST2;
|
||||
#Stat.FP_AST2 := #ZylGst AND #Stat.Typ5_3;
|
||||
IF #tFlanke THEN
|
||||
#Stat.Statistik.AST2_Schaltungen := #Stat.Statistik.AST2_Schaltungen + 1;
|
||||
END_IF;
|
||||
END_REGION
|
||||
END_FUNCTION_BLOCK
|
||||
@@ -0,0 +1,91 @@
|
||||
# SCL-Export — AWL→SCL-Pilotkonvertierung
|
||||
|
||||
Pilotbausteine der AWL→SCL-Konvertierung, bereit zum Test in TIA Portal V20.
|
||||
Je Station ein Ordner; die Dateien sind **externe Quellen** (Import über
|
||||
*Externe Quellen → Quelle hinzufügen → Bausteine aus Quelle generieren*).
|
||||
|
||||
## Inhalt
|
||||
|
||||
| Baustein | Original | Kategorie | Datei |
|
||||
|---|---|---|---|
|
||||
| MZylinder | FB 92, AWL, 11 Netzwerke | A — Verknüpfungslogik | `MZylinder.scl` |
|
||||
| FifoIn | FC 80, AWL, Pointer/BLKMOV | C — Pointerzugriffe | `FifoIn.scl` |
|
||||
| DatenOut | FC 86, AWL, Pointer/indirekte Aufrufe | C — Pointerzugriffe | `DatenOut.scl` |
|
||||
| MHMOmniSchleuse | FB 1274, AWL, Schrittkette 52 NW | B — Ablaufsteuerung | `MHMOmniSchleuse.scl` |
|
||||
|
||||
Die Bausteine sind zwischen UH06 und UH07 identisch (FifoIn: nur Kommentare
|
||||
verschieden) — **Ausnahme `MHMOmniSchleuse`**: die CASE-Verteiler für die
|
||||
indirekten FC-Aufrufe (Leser-/Ziel-/Draussen-FC) enthalten die
|
||||
stationsspezifischen FC-Nummern:
|
||||
- UH06: 181/1181/2181 (Ziel), 1180 (Leser), 1182 (Draussen)
|
||||
- UH07: 181..6181 (Ziel), 180..6180 (Leser), 182..6182 (Draussen)
|
||||
|
||||
## WICHTIG: Nicht direkt ins Produktivprojekt!
|
||||
|
||||
`DatenOut` und `FifoIn` haben geänderte Schnittstellen
|
||||
(`POINTER`/`ANY` → `VARIANT`, S7-1500-konform). Die **bestehenden
|
||||
AWL-Aufrufer** übergeben `P#M...`-Pointer und kompilieren nach dem Ersetzen
|
||||
u.U. nicht mehr, bis sie angepasst sind. Daher:
|
||||
|
||||
1. **Projektkopie** anlegen (oder eigenes Test-Projekt mit PLCSIM).
|
||||
2. Quellen importieren und **einzeln generieren** — Reihenfolge:
|
||||
`DatenOut` → `MHMOmniSchleuse` (Aufrufer von DatenOut); `MZylinder` und
|
||||
`FifoIn` sind unabhängig.
|
||||
3. Kompilieren; Fehlerliste prüfen (erwartbar: Aufrufer von DatenOut/FifoIn,
|
||||
die noch AWL-Pointer übergeben).
|
||||
|
||||
`MZylinder` ist schnittstellengleich und kann den AWL-Baustein direkt
|
||||
ersetzen (Aufrufer und Instanz-DBs bleiben gültig, neu übersetzen genügt).
|
||||
|
||||
## Voraussetzungen (im Projekt bereits vorhanden)
|
||||
|
||||
- UDTs: `UZylinder`, `UDLHMOmniSchleuse`, `USk_ILS`, `UErrHMOmniSchleuse`, `UTrolley`
|
||||
- Bausteine: `TEST_DB_TO_ATTR_DB` (SCL), `BCD (4stellig)_to_INT`,
|
||||
`Wandel_FC_für_DB1000`, `Trolley_DL_Neue_Pos`, die Leser-/Ziel-/Draussen-FCs,
|
||||
DB `TrolleyDL_OutDaten` (DB1011)
|
||||
- PLC-Variablen: `MW2`, `MW4`, `100ms-Impuls`, `1s-Impuls`, `MW200`,
|
||||
`M200.0..M201.7`, `MW190..MW198`, `MD182`, `MD192`, `M180.x`, `M199.0`,
|
||||
`MW248..MW254`
|
||||
- Nicht-optimierte DBs (Standard-Zugriff) — die Bausteine arbeiten mit
|
||||
PEEK/POKE auf absolute Byte-Offsets, Layouts unverändert.
|
||||
|
||||
## Umsetzungs-Konventionen (gelten auch für die Massenkonvertierung)
|
||||
|
||||
- AWL-Netzwerke → `REGION NW<x>: <Titel>`; Sprünge → `IF`/`CASE`/`GOTO`
|
||||
(GOTO nur für die gemeinsamen Fehler-/Verwaltungs-Enden, wie im AWL).
|
||||
- `OPN DB[n]` + `DBW[AR1,..]` → `PEEK/POKE (area:=16#84, dbNumber:=…, byteOffset:=…)`.
|
||||
- `BLKMOV` über ANY-Pointer → `Serialize`/`Deserialize` mit `VARIANT`-Parametern.
|
||||
- BR-Bit (`SAVE`/`JBI`/`BEC`) → `ENO := …;` bzw. Auswertung von RE_VAL-Bit 7.
|
||||
- Indirekte Aufrufe `UC FC[n]` → `CASE n OF` mit den projektierten FC-Nummern.
|
||||
- 16-Bit-Semantik (`+I`, `*I`) bleibt `Int`; keine stille Aufweitung auf DInt.
|
||||
|
||||
## Bekannte, dokumentierte Abweichungen (Kopf der jeweiligen Datei!)
|
||||
|
||||
- **FifoIn**: `INDATEN` jetzt `Variant` (statt `POINTER`); Kopier-Fehler des
|
||||
alten SFC20 werden über eigene Bereichsprüfungen nachgebildet.
|
||||
- **DatenOut**: `SEARCHDATEN`/`OUTDATEN` jetzt `Variant`; die DB-Prüfungen des
|
||||
Such-/Out-DB (AWL NW5/NW8) entfallen (Fehlerbit 5 wird nicht mehr erzeugt);
|
||||
`OUTDATEN`-Ziel sollte exakt Datensatzlänge×2 Bytes groß sein;
|
||||
`Position` wird mit 0 initialisiert (AWL: undefiniert).
|
||||
- **MHMOmniSchleuse**: Merker-Scratch (MW200-Suchdaten, M220-OutDaten, MB199)
|
||||
durch lokale Puffer ersetzt; Merker-Schnittstellen zu Fremd-FCs
|
||||
(MW190–MW198, MD182/192, M180.x, Fehlerwort MW200/M200.x/M201.x) bleiben.
|
||||
NW21 war im Original tot (CLR+JU) und entfällt. In Schritt 10 („NoLesung")
|
||||
verglich das AWL gegen einen **undefinierten Akku-Altwert** — umgesetzt ist
|
||||
die beabsichtigte Prüfung `TrolleyID_Input <> 0`. **Bei IBN prüfen!**
|
||||
|
||||
## Empfohlener Vergleichstest (PLCSIM)
|
||||
|
||||
1. AWL-Original umbenennen (z.B. `DatenOut` → `DatenOut_AWL`), SCL-Baustein
|
||||
generieren.
|
||||
2. Test-FC schreiben, der beide mit identischen Eingangsdaten aufruft
|
||||
(getrennte Kopien des Ziel-DB!) und RE_VAL/ENO/DB-Inhalte vergleicht.
|
||||
3. Für `MHMOmniSchleuse`: Instanz-DBs beider Varianten nach jedem Zyklus
|
||||
vergleichen (Watch-Tabelle bzw. Vergleichs-FC über die Static-Bereiche).
|
||||
|
||||
## Werkzeug
|
||||
|
||||
`tools/stlxml2awl.py` wandelt die SimaticML-XML-Exporte (`StatementList`) in
|
||||
lesbaren AWL-Text um (`python stlxml2awl.py <baustein.xml>`), inkl. Interface,
|
||||
Netzwerktitel, Sprungmarken und Kommentaren — Grundlage für die
|
||||
Massenkonvertierung der restlichen ~230 Bausteine.
|
||||
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Wandelt TIA-Openness SimaticML XML (StatementList) in lesbaren AWL-Text um."""
|
||||
import re
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
NS_IF = "{http://www.siemens.com/automation/Openness/SW/Interface/v5}"
|
||||
NS_SL = "{http://www.siemens.com/automation/Openness/SW/NetworkSource/StatementList/v5}"
|
||||
|
||||
TOKEN_MAP = {
|
||||
"A_BRACK": "A(", "O_BRACK": "O(", "AN_BRACK": "AN(", "ON_BRACK": "ON(",
|
||||
"X_BRACK": "X(", "XN_BRACK": "XN(", "BRACKET": ")",
|
||||
"NE_I": "<>I", "EQ_I": "==I", "GT_I": ">I", "LT_I": "<I", "GE_I": ">=I", "LE_I": "<=I",
|
||||
"NE_D": "<>D", "EQ_D": "==D", "GT_D": ">D", "LT_D": "<D", "GE_D": ">=D", "LE_D": "<=D",
|
||||
"NE_R": "<>R", "EQ_R": "==R", "GT_R": ">R", "LT_R": "<R", "GE_R": ">=R", "LE_R": "<=R",
|
||||
"ADD_I": "+I", "SUB_I": "-I", "MUL_I": "*I", "DIV_I": "/I",
|
||||
"ADD_D": "+D", "SUB_D": "-D", "MUL_D": "*D", "DIV_D": "/D",
|
||||
"ADD_R": "+R", "SUB_R": "-R", "MUL_R": "*R", "DIV_R": "/R",
|
||||
"ADD": "+", "NOP_0": "NOP 0", "NOP_1": "NOP 1",
|
||||
"TAR2_ACCU1": "TAR2", "TAR1_ACCU1": "TAR1",
|
||||
"LAR1_ACCU1": "LAR1", "LAR2_ACCU1": "LAR2",
|
||||
"OPEN_DB": "OPN", "OPEN_DI": "OPN",
|
||||
"EMPTY_LINE": "", "COMMENT": "//", "Assign": "=",
|
||||
}
|
||||
|
||||
|
||||
def fmt_symbol(access):
|
||||
parts = []
|
||||
for comp in access.findall(f"./{NS_SL}Symbol/{NS_SL}Component"):
|
||||
name = comp.get("Name")
|
||||
token = name if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", name) else f'"{name}"'
|
||||
idx = [fmt_access(a) for a in comp.findall(f"{NS_SL}Access")]
|
||||
if idx:
|
||||
token += "[" + ",".join(idx) + "]"
|
||||
if comp.get("SliceAccessModifier"):
|
||||
token += "." + comp.get("SliceAccessModifier")
|
||||
parts.append(token)
|
||||
sym = ".".join(parts)
|
||||
if access.get("Scope") == "GlobalVariable" and parts and not parts[0].startswith('"'):
|
||||
sym = f'"{parts[0][1:-1] if parts[0].startswith(chr(34)) else parts[0]}"' + (
|
||||
"." + ".".join(parts[1:]) if len(parts) > 1 else "")
|
||||
return sym
|
||||
|
||||
|
||||
def fmt_address(addr):
|
||||
area = addr.get("Area")
|
||||
typ = addr.get("Type")
|
||||
bit = int(addr.get("BitOffset", 0))
|
||||
blk = addr.get("BlockNumber")
|
||||
width = {"Bool": 1, "Byte": 8, "Word": 16, "DWord": 32, "Int": 16, "DInt": 32}.get(typ, 0)
|
||||
sizech = {1: "", 8: "B", 16: "W", 32: "D"}.get(width, "?")
|
||||
areach = {"Local": "L", "Memory": "M", "Input": "I", "Output": "Q", "DB": "DB", "DI": "DI",
|
||||
"Timer": "T", "Counter": "C", "PeripheryInput": "PI", "PeripheryOutput": "PQ"}.get(area, area)
|
||||
if area in ("DB", "DI"):
|
||||
pre = f"DB{blk}." if blk else ""
|
||||
core = f"{pre}{'DBX' if width == 1 else 'DB' + sizech}{bit // 8}"
|
||||
elif area == "Timer":
|
||||
return f"T {bit}"
|
||||
elif area == "Counter":
|
||||
return f"C {bit}"
|
||||
else:
|
||||
core = f"{areach}{sizech}{bit // 8}"
|
||||
if width == 1:
|
||||
core += f".{bit % 8}"
|
||||
return core
|
||||
|
||||
|
||||
def fmt_indirect(ind):
|
||||
width = ind.get("Width")
|
||||
area = ind.get("Area")
|
||||
reg = ind.get("Register", "")
|
||||
inner = [fmt_access(a) for a in ind.findall(f"{NS_SL}Access")]
|
||||
const = ind.find(f"{NS_SL}Constant")
|
||||
off = ""
|
||||
if const is not None:
|
||||
off = const.findtext(f"{NS_SL}ConstantValue", "")
|
||||
if width == "Block": # OPN DB[var]
|
||||
return f"DB[{inner[0]}]" if inner else f"DB[{off}]"
|
||||
sizech = {"Bit": "X", "Byte": "B", "Word": "W", "Double": "D", "DWord": "D"}.get(width, "?")
|
||||
areach = {"DB": "DB", "DI": "DI", "Local": "L", "Memory": "M", "Input": "I", "Output": "Q"}.get(area, area or "")
|
||||
base = f"{areach}{sizech}" if areach not in ("DB", "DI") else f"{areach}{sizech}"
|
||||
if reg: # registerindirekt
|
||||
return f"{base} [{reg},{off}]"
|
||||
if inner: # speicherindirekt
|
||||
return f"{base} [{inner[0]}]"
|
||||
return f"{base} [{off}]"
|
||||
|
||||
|
||||
def fmt_access(access):
|
||||
scope = access.get("Scope")
|
||||
if scope in ("LocalVariable", "GlobalVariable", "LocalConstant", "GlobalConstant"):
|
||||
return fmt_symbol(access)
|
||||
if scope in ("LiteralConstant", "TypedConstant"):
|
||||
c = access.find(f"{NS_SL}Constant")
|
||||
val = c.findtext(f"{NS_SL}ConstantValue", "") if c is not None else ""
|
||||
typ = c.findtext(f"{NS_SL}ConstantType", "") if c is not None else ""
|
||||
pref = {"Word": "W#16#", "DWord": "DW#16#", "Time": "", "S5Time": "", "Byte": "B#16#"}
|
||||
if typ in ("Word", "DWord", "Byte") and not val.startswith(("2#", "16#", "W#", "DW#", "B#")):
|
||||
return pref[typ] + val
|
||||
return val
|
||||
if scope == "AddressConstant":
|
||||
c = access.find(f"{NS_SL}Constant")
|
||||
return "P#" + c.findtext(f"{NS_SL}ConstantValue", "") if c is not None else "P#?"
|
||||
if scope == "Address":
|
||||
addr = access.find(f"{NS_SL}Address")
|
||||
if addr is not None:
|
||||
return fmt_address(addr)
|
||||
ind = access.find(f"{NS_SL}Indirect")
|
||||
if ind is not None:
|
||||
return fmt_indirect(ind)
|
||||
return "?ADDR?"
|
||||
if scope == "Label":
|
||||
lab = access.find(f"{NS_SL}Label")
|
||||
return lab.get("Name") if lab is not None else "?LBL?"
|
||||
if scope == "LabelDefinition":
|
||||
lab = access.find(f"{NS_SL}Label")
|
||||
return (lab.get("Name") + ":") if lab is not None else "?LBL?:"
|
||||
if scope == "Statusword":
|
||||
return access.find(f"{NS_SL}Statusword").get("Name", "?SW?")
|
||||
if scope == "Call":
|
||||
ci = access.find(f"{NS_SL}CallInfo")
|
||||
if ci is None:
|
||||
ins = access.find(f"{NS_SL}Instruction")
|
||||
name = ins.get("Name")
|
||||
params = []
|
||||
for p in ins.findall(f"{NS_SL}Parameter"):
|
||||
pa = p.find(f"{NS_SL}Access")
|
||||
params.append(f"{p.get('Name')}:={fmt_access(pa) if pa is not None else '?'}")
|
||||
return f"CALLINFO SYS \"{name}\" (" + ", ".join(params) + ")"
|
||||
name, btype = ci.get("Name"), ci.get("BlockType")
|
||||
inst = ci.find(f"{NS_SL}Instance")
|
||||
s = f'"{name}"'
|
||||
if inst is not None:
|
||||
s += " , " + fmt_symbol(inst)
|
||||
params = []
|
||||
for p in ci.findall(f"{NS_SL}Parameter"):
|
||||
pa = p.find(f"{NS_SL}Access")
|
||||
params.append(f"{p.get('Name')}:={fmt_access(pa) if pa is not None else '?'}")
|
||||
if params:
|
||||
s += " (" + ", ".join(params) + ")"
|
||||
return f"CALLINFO {btype} {s}"
|
||||
return f"?{scope}?"
|
||||
|
||||
|
||||
def render_statement(st):
|
||||
label = ""
|
||||
ld = st.find(f"{NS_SL}LabelDeclaration/{NS_SL}Label")
|
||||
if ld is not None:
|
||||
label = ld.get("Name") + ": "
|
||||
toks = st.findall(f"{NS_SL}StlToken")
|
||||
accesses = st.findall(f"{NS_SL}Access")
|
||||
comments = []
|
||||
for lc in st.iter(f"{NS_SL}LineComment"):
|
||||
t = lc.findtext(f"{NS_SL}Text", "")
|
||||
if t:
|
||||
comments.append(t)
|
||||
for cm in st.findall(f"{NS_SL}Comment"):
|
||||
t = cm.findtext(f"{NS_SL}Text", "")
|
||||
if t:
|
||||
comments.append(t)
|
||||
if not toks:
|
||||
if label:
|
||||
return label.rstrip()
|
||||
return "// " + " | ".join(comments) if comments else ""
|
||||
tok = toks[0].get("Text")
|
||||
mn = TOKEN_MAP.get(tok, tok)
|
||||
if tok == "EMPTY_LINE":
|
||||
return label.rstrip() if label else ""
|
||||
if tok == "COMMENT":
|
||||
return "// " + " | ".join(comments)
|
||||
ops = []
|
||||
for a in accesses:
|
||||
f = fmt_access(a)
|
||||
if f.startswith("CALLINFO"):
|
||||
return f"{label}CALL {f[9:]}" + (" // " + " | ".join(comments) if comments else "")
|
||||
ops.append(f)
|
||||
line = f"{label:6s}{mn:6s} " + ", ".join(ops)
|
||||
if comments:
|
||||
line = f"{line:60s} // " + " | ".join(comments)
|
||||
return line.rstrip()
|
||||
|
||||
|
||||
def render_interface(root):
|
||||
out = []
|
||||
for sec in root.iter(f"{NS_IF}Section"):
|
||||
members = sec.findall(f"{NS_IF}Member")
|
||||
out.append(f" {sec.get('Name')}:")
|
||||
for m in members:
|
||||
out.extend(render_member(m, 4))
|
||||
return out
|
||||
|
||||
|
||||
def render_member(m, indent):
|
||||
out = []
|
||||
cm = m.find(f"{NS_IF}Comment/{NS_IF}MultiLanguageText")
|
||||
sv = m.findtext(f"{NS_IF}StartValue")
|
||||
line = " " * indent + f"{m.get('Name')} : {m.get('Datatype')}"
|
||||
if sv:
|
||||
line += f" := {sv}"
|
||||
if cm is not None and cm.text:
|
||||
line = f"{line:55s} // {cm.text}"
|
||||
out.append(line)
|
||||
for sub in m.findall(f"{NS_IF}Member"):
|
||||
out.extend(render_member(sub, indent + 2))
|
||||
return out
|
||||
|
||||
|
||||
def main(path):
|
||||
s = open(path, encoding="utf-8").read()
|
||||
root = ET.fromstring(s)
|
||||
blk = None
|
||||
for tag in ("FB", "FC", "OB", "GlobalDB", "InstanceDB"):
|
||||
blk = root.find(f".//SW.Blocks.{tag}")
|
||||
if blk is not None:
|
||||
btype = tag
|
||||
break
|
||||
attrs = blk.find("AttributeList")
|
||||
name = attrs.findtext("Name")
|
||||
num = attrs.findtext("Number")
|
||||
lang = attrs.findtext("ProgrammingLanguage")
|
||||
print(f"=== {btype} {num}: {name} [{lang}] ===")
|
||||
iface = attrs.find("Interface")
|
||||
if iface is not None:
|
||||
secs = iface.find(f"{NS_IF}Sections")
|
||||
if secs is not None:
|
||||
print("INTERFACE:")
|
||||
for line in render_interface(secs):
|
||||
print(line)
|
||||
nwno = 0
|
||||
for cu in blk.iter("SW.Blocks.CompileUnit"):
|
||||
nwno += 1
|
||||
title = ""
|
||||
for mt in cu.iter("MultilingualTextItem"):
|
||||
al = mt.find("AttributeList")
|
||||
if al is not None and al.findtext("Culture") == "de-DE" and al.findtext("Text"):
|
||||
title = al.findtext("Text")
|
||||
break
|
||||
print(f"\n--- NW {nwno}: {title} ---")
|
||||
src = cu.find(f".//{NS_SL}StatementList")
|
||||
if src is None:
|
||||
print(" (kein STL-Quelltext)")
|
||||
continue
|
||||
for st in src.findall(f"{NS_SL}StlStatement"):
|
||||
print(render_statement(st))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for p in sys.argv[1:]:
|
||||
main(p)
|
||||
Reference in New Issue
Block a user