jede Minute einen Datensatz aufzeichnen
Hallo SPS_LB,
ich denke, für Deine Aufgabe mit 2880 INT-Werten ist ein FIFO nicht gut geeignet.
5758 Byte komplett umspeichern, nur um 2 Byte der Liste hinzuzufügen ... tststs
Hast Du mal im CPU-Baugruppenzustand auf die Zykluszeiten geschaut?
Wenn es schon unbedingt ein FIFO sein muß, dann sollte das Einspeichern in den FIFO wenigstens effizient
programmiert sein.
Das Beispielprogramm von
Kai benötigt dafür auf einer aktuellen CPU 315 immerhin 19,3 ms.
Wenn man die unnötigen Berechnungen und das Zwischenspeichern aus der Schleife verbannt, dann läßt
sich die Bearbeitungszeit auf 16,2 ms drücken.
Wenn man immer gleich 2 INT-Werte auf einmal umspeichert, dann braucht man nur noch 8,7 ms.
Und mit Einsatz der SFC20 "BLKMOV" sinkt das Ganze auf 2,7 ms.
(weil SFC20 immer aufsteigend kopiert, ist dann der älteste Wert im FIFO vorn und der neueste hinten)
Ringpuffer
Deine Aufgabe beschreibt eigentlich den klassischen Fall, eine Variable (z.B. Meßwert) über 48 Stunden
mit minütlicher Abtastung in ein Archiv für eine Trendkurve aufzuzeichnen.
Wenn in dem Archiv nicht zwingend der älteste Wert ganz vorn in Wert[1] stehen muß (wie z.B. bei
Trendkurven für WinCC flexible), dann ist ein Ringpuffer mit einem Zeiger auf die aktuelle Einfügeposition
am besten geeignet. Da wird einfach nur der neue Wert an der Zeigerposition eingefügt und der Einfügezeiger
weitergestellt. Der älteste Wert im Ringpuffer wird überschrieben. Das dauert nur 0,025 ms.
minütliche Aufzeichnung von Datensätzen in einen Ringpuffer
Wenn ich so eine Aufzeichnung mache, dann berechne ich die Einfügeposition aus der Uhrzeit:
1.Tag um 0:00 Uhr in Datensatz[0], um 0:01 Uhr in Datensatz[1] ... 23:59 Uhr in Datensatz[1439]
2.Tag um 0:00 Uhr in Datensatz[1440] ... 23:59 Uhr in Datensatz[2879]
Sobald 48 Stunden aufgezeichnet wurden, enthält der Archiv-DB jederzeit die Werte der letzten 48 Stunden.
Der Archiv-DB enthält außer den Datensätzen noch den Index und den Zeitstempel der letzten Aufzeichnung.
Mit diesen Angaben kann nach dem Auslesen des Archiv-DB die Trendkurve mit Zeitangaben erstellt werden.
Zum Auslesen benutze ich Excel/VBA und LibnoDave. Das Excel-Makro bringt die Datensätze in die richtige
Reihenfolge (anhand Index der letzten Aufzeichnung) und schreibt die Datensätze in eine CSV-Datei.
Mit Excel kann man auch gleich die Trendkurve in einem Diagramm darstellen.
Im Anhang die ausführlich kommentierte Quelle eines in der Praxis bewährtes Aufzeichnungsprogramms,
angepaßt für nur 1 INT-Wert. Hier der Programmcode FC105 (Kommentare und DB105 gekürzt):
Code:
FUNCTION FC105 : VOID
TITLE =jede Minute einen Datensatz in einen Ringpuffer aufzeichnen
AUTHOR : PN_DP
VAR_TEMP
act_Time : DATE_AND_TIME ;
Temp_Int : INT ;
DS_Nr : INT ;
END_VAR
BEGIN
NETWORK
TITLE =Trigger
// aktuelle Uhrzeit holen
CALL "READ_CLK" ( //SFC1
RET_VAL := #Temp_Int,
CDT := #act_Time);
// Datensatz-Index aus Uhrzeit berechnen
L P##act_Time; //Adresse #act_Time
LAR1 ;
L LB [AR1,P#3.0]; //aktuelle Stunde BCD
BTI ;
L 60;
*I ;
L LB [AR1,P#4.0]; //aktuelle Minute BCD
BTI ;
+I ;
T #DS_Nr; //0:00..23:59 -> 0...1439
// Trigger: neue Minute?
L "Archiv".DS_Nr; //Index letzte Aufzeichnung
L 1440; //Anzahl Datensätze je Tag
MOD ; //Index letzte Aufzeichnung heute
L #DS_Nr; //jetzt 0:00..23:59 -> 0...1439
==I ; //in dieser Minute schon aufgezeichnet?
BEB ; //ja, nicht wieder aufzeichnen
NETWORK
TITLE =Datensatz-Adresse berechnen
// Datensatz-Nr aus Uhrzeit: 0:00 Uhr = 0 bis 23:59 Uhr = 1439
L B#16#0;
L #DS_Nr; //0..1439
<>I ;
SPB CKDS; //springe, wenn <> 0:00
// es ist 0:00 Uhr! -> Tag-Wechsel (und ggf. DB-Wechsel)
UN "Archiv".Tag2;
= "Archiv".Tag2;
CKDS: L 1440; //-> AKKU1:1440 AKKU2:DS_Nr
>=D ; //darf nur 0..1439
BEB ; //(kommt bei unsachgemäß beobachten vor)
TAK ; //-> AKKU1:DS_Nr AKKU2:1440
U "Archiv".Tag2;
SPBN TPTR;
+I ; // + 1440 (24*60)
TPTR: T "Archiv".DS_Nr; //0..2879 neue Schreib-DS_Nr
// DB-Pointer aus Datensatz-Nr berechnen (AKKU1:DS_Nr 0..2879)
L 2; //Länge eines Datensatzes
*I ; //-> Startadresse des Datensatzes
SLD 3; //-> P#0.0..P#5758.0
LAR1 ; //-> Pointer DS[DS_Nr]
NETWORK
TITLE =Datensatz aufzeichnen
L MW 102; //aufzuzeichnender Wert
AUF "Archiv";
T DBW [AR1,P#0.0]; //ins Archiv schreiben
// Zeitstempel
CALL "BLKMOV" ( //SFC20
SRCBLK := #act_Time,
RET_VAL := #Temp_Int,
DSTBLK := "Archiv".DS_TimeStamp);
END_FUNCTION
//Der DB105 (gekürzt):
DATA_BLOCK "Archiv"
STRUCT
DS : ARRAY [0 .. 2879 ] OF //48-Stunden-Archiv, minütliche Datensätze
STRUCT
Wert1 : INT ;
END_STRUCT ;
DS_TimeStamp : DATE_AND_TIME ; //Zeitstempel zuletzt geschriebener Datensatz
DS_Nr : INT ; //Index zuletzt geschriebener Datensatz
Tag2 : BOOL ; //0=1.Tag / 1=2.Tag
END_STRUCT ;
END_DATA_BLOCK
btw: Die CPU-Uhr sollte mit einer geeichten Uhr synchronisiert werden, z.B. mit einem NTP-Server.
Die CPU-Uhr sollte immer auf Winterzeit stehen.
Gruß
PN/DP