Eindimensionales Array beschreiben

SPS_LB

Level-1
Beiträge
8
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

Ich habe ein ARRAY[1..2880] vom Typ INT angelegt und möchte dieses Array schrittweise beschreiben.
Mit einem Minutenimpuls soll ein Messwert hier reingeschrieben werden. Wie bei einem FIFO soll dann der älteste einfach hinten rausfallen.

Wie realisiere ich es am einfachsten? Ich kann das natürlich händisch mit MOVE machen, ist aber für 2880 Werte sicher keine wirkliche Lösung.

Vielen Dank im Vorraus.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe ein ARRAY[1..2880] vom Typ INT angelegt und möchte dieses Array schrittweise beschreiben.
Mit einem Minutenimpuls soll ein Messwert hier reingeschrieben werden. Wie bei einem FIFO soll dann der älteste einfach hinten rausfallen.

Hier ist einmal ein kurzes Programmbeispiel für einen FIFO-Speicher:

Code:
FUNCTION FC 100 : VOID
TITLE =FIFO Werte
AUTHOR : KAI
FAMILY : SPSFORUM
NAME : FIFO
VERSION : 1.0
 
VAR_INPUT
  DB_Werte : BLOCK_DB ; 
  Anzahl_Werte : INT ; 
  Wert : INT ; 
END_VAR
VAR_TEMP
  DB_Register : WORD ; 
  AR1_Register : DWORD ; 
  Zwischenwert : INT ; 
  Schleife : INT ; 
END_VAR
BEGIN
NETWORK
TITLE =Register sichern
 
      L     DBNO; // DB-Register
      T     #DB_Register; 
 
      TAR1  ; // AR1-Register
      T     #AR1_Register; 
 
NETWORK
TITLE =FIFO Werte
 
      AUF   #DB_Werte; // DB-Werte
 
      L     #Wert; // Wert
      T     #Zwischenwert; // Zwischenwert
 
      L     P#0.0; 
      LAR1  ; 
 
      L     #Anzahl_Werte; // Anzahl Werte
M01:  T     #Schleife; 
 
      L     DBW [AR1,P#0.0]; // Wert
      L     #Zwischenwert; // Zwischenwert
      T     DBW [AR1,P#0.0]; // Zwischenwert -> Wert   
      TAK   ; 
      T     #Zwischenwert; // Wert -> Zwischenwert
 
      L     P#2.0; 
      +AR1  ; 
 
      L     #Schleife; 
      LOOP  M01; 
 
NETWORK
TITLE =Register wiederherstellen
 
      AUF   DB [#DB_Register]; // DB-Register
 
      L     #AR1_Register; // AR1-Register
      LAR1  ; 
 
END_FUNCTION

Gruß Kai
 

Anhänge

  • OB1.pdf
    6,1 KB · Aufrufe: 126
  • FC100.pdf
    6,5 KB · Aufrufe: 141
  • DB100.pdf
    4,4 KB · Aufrufe: 100
  • Fifo.zip
    31,9 KB · Aufrufe: 100
Und noch zwei Bilder aus der Simulation mit PLCSIM.

Gruß Kai
 

Anhänge

  • PLCSIM_1.jpg
    PLCSIM_1.jpg
    219,4 KB · Aufrufe: 100
  • PLCSIM_2.jpg
    PLCSIM_2.jpg
    219,9 KB · Aufrufe: 79
Zuviel Werbung?
-> Hier kostenlos registrieren
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
 

Anhänge

  • Aufzeichnung.awl.zip
    9,1 KB · Aufrufe: 51
Zurück
Oben