TIA Ereignis in den letzten X Minuten Y mal aufgetreten

Mok_1988

Level-2
Beiträge
295
Reaktionspunkte
65
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen

Ich bräuchte eine Funktion, die mir die Häufigkeit eines bestimmten Ereignisses in den letzten X Minuten zurück gibt.
Dabei möchte ich nicht einen Timer mit 10 Minuten starten, und in der Zeit das Ereignis zählen, sondern ich möchte schon zu jeder Zeit wissen, wie oft dieses Ereignis in den letzten 10 Minuten aufgetreten ist.

Mein Ansatz wäre nun einen FB mit einer Anzahl an Timer anzulegen (zunächst erstmal z.B. 10 Timer) und jedem Timer die 10 Minuten geben. Bei jeden Eintritt des Ereignisses wird ein Timer gestartet und anschließend nur die Anzahl der aktiven Timer zählen

Habt ihr eine bessere und einfachere Idee? Denke ich gerade zu kompliziert?

TIA V18 mit einer 1500er Steuerung

Grüße
 
Du könntest einen Timer mit maximal einstellbarer Zeit starten und bei jedem Ereignis die aktuell abgelaufene Zeit mit wegspeichern.
Wenn du dann wissen möchtest, wieviele Ereignisse in den letzten z.B. 10min aufgetreten sind, prüfst/zählst du die Zeiten der Ereignisse ob/wenn (> aktuelle Zeit - 10min und < aktuelle Zeit)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Du könntest einen Timer mit maximal einstellbarer Zeit starten und bei jedem Ereignis die aktuell abgelaufene Zeit mit wegspeichern.
Wenn du dann wissen möchtest, wieviele Ereignisse in den letzten z.B. 10min aufgetreten sind, prüfst/zählst du die Zeiten der Ereignisse ob/wenn (> aktuelle Zeit - 10min und < aktuelle Zeit)
Da würde ich aber eher ein FiFo Array machen mit der Uhrzeit dann kannst du die Zeit auch ändern. Kommt natürlich drauf an wie groß das Array ist.
 
Sowas gehört ja eigentlich in eine Datenbank, finde ich.
Alle 10 Minuten den aktuellen Zähler abschicken und dann kann es sauber ausgewertet werden.

So hättest du auf 24 Stunden gesehen 144 * 10 Werte. Mit einem USInt (max. Anzahl wäre hier 255, länge 8bit) geht das ja noch. Das könntest du auch so machen.

Ich würde das einfach mit einem Timer machen und einfach hochzählen und das ganze umladen und das Fach eins hochzählen.

Vielleicht kannst du es ja auch nach Schichten noch aufteilen.
 
Wenn die Bedingungen nicht weiter eingegrenzt werden können, dann geht das prinzipiell nur mit einem Protokoll/einer Liste mit Einträgen mit Zeitstempeln, wo nachträglich für gewünschte Zeiträume die passenden Einträge gezählt werden.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich würde ein FIFO-Array anlegen, das immer mit einem bestimmten Zeittakt (beispielsweise jede Minute) weitergeschoben wird. Reingezählt wird immer in das erste Element und du summierst dann immer alle (z.B. 10) Elemente auf.
Natürlich hat man dabei eine Ungenauigkeit von einem Zeittakt (direkt nach dem Schieben summierst du effektiv ja nur die letzten 9 Minuten auf, kurz vor dem Schieben die letzten 10 Minuten). Wenn man das konstanter und genauer haben möchte, muss man den Zeittakt runtersetzen (z.B. alle 10 Sekunden oder jede Sekunde), der Speicherbedarf wird dadurch entsprechend größer.
 
Zuletzt bearbeitet:
Wenn die Bedingungen nicht weiter eingegrenzt werden können, dann geht das prinzipiell nur mit einem Protokoll/einer Liste mit Einträgen mit Zeitstempeln, wo nachträglich für gewünschte Zeiträume die passenden Einträge gezählt werden.
Richtig, wenn es vernünftig funktionieren soll geht es eig nur so.
Bei jedem Ereignis das Array durchgehen und die Ereignisse zählen die innerhalb der letzten 10 Minuten liegen. Außerdem alle Ereignisse aus dem Array löschen die älter als 10 Minuten sind.
 
.. Mein Ansatz wäre nun einen FB mit einer Anzahl an Timer anzulegen (zunächst erstmal z.B. 10 Timer) und jedem Timer die 10 Minuten geben. Bei jeden Eintritt des Ereignisses wird ein Timer gestartet und anschließend nur die Anzahl der aktiven Timer zählen ..
Ich würde es ähnlich machen. Allerdings nicht mit einem Sack voller Timer, sondern (wir müssen ja sparen) mit einem ARRAY of Time, Real etc. und mit ARRAYs of Bool für die Zustände und Flanken. Mit "RUNTIME" (da haben wir's wieder ☺️) wird die Abtastzeit ermittelt, mit derer die aktiven Zeiten hochgezählt werden. Eventuell genügt es, anstatt in einer FOR-Schleife, pro Programmzyklus nur eine Zeit zu bearbeiten.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Danke für eure Anregungen.

Ich würde es ähnlich machen. Allerdings nicht mit einem Sack voller Timer, sondern (wir müssen ja sparen) mit einem ARRAY of Time, Real etc. und mit ARRAYs of Bool für die Zustände und Flanken. Mit "RUNTIME" (da haben wir's wieder ☺️) wird die Abtastzeit ermittelt, mit derer die aktiven Zeiten hochgezählt werden. Eventuell genügt es, anstatt in einer FOR-Schleife, pro Programmzyklus nur eine Zeit zu bearbeiten.

Bei meiner Anwendung sind nicht soviele Ereignisse zu erwarten innerhalb von 10 Minuten. Ich hatte zunächst gedacht einen skalierbaren Baustein zu erstellen. Aber nun habe ich es sehr einfach gehalten und es so umgesetzt wie ich es mir zuerst gedacht habe.

Also mit 10 Timern sollte ich für meinen Fall wohl auskommen.
 
... ich hätte allerdings auch eher, so wie @blackpeat im Beitrag #3 geschrieben hat, ein FIFO gemacht, in das ich immer den aktuellesten Zeitstempel vorne reinschiebe. Dieses Konstrukt würde dir dein Vorhaben auf alle Fälle und auch schön variabel ermöglichen ...

(das mit Timern zu versuchen wäre mir jetzt zu kompliziert ... ;) )
 
Zuletzt bearbeitet:
Hier ein auf die Schnelle zusammengestrickter (Copy/Pasta mit Erweiterungen) Ansatz.
Ein Signal (BOOL) wird gezählt, und zwar wie oft es in einer Sekunde auftrat.
Anschließend werden die Zahlen in Arrays geschrieben, die die aktuelle Sekunde/Minute/Stunde und Tag repräsentieren.

Wegen der Forderung nach den letzten 10 Minuten gibt es noch einen Block der die letzten 60 Minuten in 6 Blöcke je 10 Minuten zusammenfasst.


Code:
FUNCTION_BLOCK "Analyse_FB"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT
      Signal : Bool;
   END_VAR

   VAR
      FL_Signal : Bool;
   END_VAR
   VAR RETAIN
      Count : UDInt;
      Gegenwart {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
      Vergangenheit {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
      PerSec : Array[0..59] of UDInt;
      PerMin : Array[0..59] of UDInt;
      PerHour : Array[0..23] of UDInt;
      PerDay : Array[0..6] of UDInt;
   END_VAR
   VAR
      ZehnMinuten : Array[0..5] of UDInt;
      StartRange : Array[0..5] of Int;
      EndRange : Array[0..5] of Int;
   END_VAR

   VAR_TEMP
      RetVal : Int;
      i : Int;
      j : Int;
      k : Int;
      Zwerg : UDInt;
   END_VAR


BEGIN
    //Systemzeit auslesen
    #RetVal := RD_LOC_T(#Gegenwart);
    
    //auf Tage zählen
    IF #Gegenwart.WEEKDAY <> #Vergangenheit.WEEKDAY THEN
        #Zwerg := 0;
        FOR #i := 0 TO 23 DO
            #Zwerg += #PerHour[#i];
        END_FOR;
        #PerDay[#Vergangenheit.WEEKDAY - 1] := #Zwerg;
    ELSE
        #Zwerg := 0;
        FOR #i := 0 TO USINT_TO_INT(#Vergangenheit.HOUR) DO
            #Zwerg += #PerHour[#i];
        END_FOR;
        #PerDay[#Gegenwart.WEEKDAY - 1] := #Zwerg;
    END_IF;
    
    //auf Stunden zählen
    IF #Gegenwart.HOUR <> #Vergangenheit.HOUR THEN
        #Zwerg := 0;
        FOR #i := 0 TO 59 DO
            #Zwerg += #PerMin[#i];
            #PerMin[#i] := 0;
        END_FOR;
        #PerHour[#Vergangenheit.HOUR] := #Zwerg;
    ELSE
        #Zwerg := 0;
        FOR #i := 0 TO USINT_TO_INT(#Vergangenheit.MINUTE) DO
            #Zwerg += #PerMin[#i];
        END_FOR;
        #PerHour[#Gegenwart.HOUR] := #Zwerg;
    END_IF;
    
    //auf Minuten zählen
    IF #Gegenwart.MINUTE <> #Vergangenheit.MINUTE THEN
        #Zwerg := 0;
        FOR #i := 0 TO 59 DO
            #Zwerg += #PerSec[#i];
        END_FOR;
        #PerMin[#Vergangenheit.MINUTE] := #Zwerg;
    ELSE
        #Zwerg := 0;
        FOR #i := 0 TO USINT_TO_INT(#Vergangenheit.SECOND) DO
            #Zwerg += #PerSec[#i];
        END_FOR;
        #PerMin[#Gegenwart.MINUTE] := #Zwerg;
    END_IF;
    
    //auf Sekunden zählen
    IF #Gegenwart.SECOND <> #Vergangenheit.SECOND THEN
        IF #Count > 0 THEN
            #PerSec[#Vergangenheit.SECOND] := #Count;
        ELSE
            #PerSec[#Vergangenheit.SECOND] := 0;
        END_IF;
        #Count := 0;
    END_IF;
    
    //innerhalb einer Sekunde zählen / Signalauswertung
    IF #Signal AND NOT #FL_Signal THEN
        #Count += 1;
    END_IF;
    #FL_Signal := #Signal; //Flanke
    
    
    REGION HMI
        //in ZehnMinuten[5] stehen die letzten Zehnminuten aufsummiert
        IF #Gegenwart.SECOND <> #Vergangenheit.SECOND THEN
            FOR #i := 0 TO 59 DO
                #j := #i / 10;
                #ZehnMinuten[#j] := 0;
                #StartRange[#j] := (#Gegenwart.MINUTE + #j * 10) MOD 60 + 1;
                #EndRange[#j] := (#Gegenwart.MINUTE + #j * 10) MOD 60 + 10;
                
                FOR #k := #StartRange[#j] TO #EndRange[#j] DO
                    IF #k <= 59 THEN
                        #ZehnMinuten[#j] += #PerMin[#k];
                    ELSE
                        #ZehnMinuten[#j] += #PerMin[#k - 60];
                    END_IF;
                END_FOR;
            END_FOR;
        END_IF;
    END_REGION
    
    //Für die Flankenbildung der Zeiten
    #Vergangenheit := #Gegenwart;
    
END_FUNCTION_BLOCK
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@zotos : vielen Dank für den Baustein. Der ist tatsächlich nicht schlecht, aber nicht ganz das was ich gesucht habe. Der Baustein betrachtet immer nur die Zeitfenster wie oft das Signal im besagten Fenster aufgetreten ist. Tritt aber nun z.B. das Signal jetzt auf und anschließend die nächsten 5 Minuten zu jeder vollen Minute wieder, habe ich nach 10 Minuten 6 Signale gesehen. Nach 10,5 Minuten sehe ich noch 5, nach 11,5 noch 4 usw

Man könnte dies aus dem Minuten Array noch extrahieren, dann hat man das auf die Minute genau, was auch ok wäre für meinen Fall.
Aber ich bleibe nun erstmal bei meinem einfachen Baustein, der funktioniert bis 10 Signale innerhalb von 10 Minuten ganz gut, das reicht mir erstmal für meine Anwendung
 
Bist du dir sicher was du willst? Es ist normal, daß die Anzahl Ereignisse nach 10 Minuten abnimmt. Das komplizierte an der Aufgabe ist die Entscheidung, wann Ereignisse aus der Zählung herausfallen, weil sie länger als 10 Minuten her sind.
 
Ja das hab ich versucht oben zu erklären, dieses Verhalten hat der Baustein aber nicht, da er nur Zeitfenster betrachtet.
Ich möchte aber genau dieses Verhalten, dass mit der Zeit die Anzahl der Ereignisse abnimmt, und nicht erst am Ende des Zeitfensters.

Passt schon so (y)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
...
Der Baustein betrachtet immer nur die Zeitfenster wie oft das Signal im besagten Fenster aufgetreten ist. Tritt aber nun z.B. das Signal jetzt auf und anschließend die nächsten 5 Minuten zu jeder vollen Minute wieder, habe ich nach 10 Minuten 6 Signale gesehen. Nach 10,5 Minuten sehe ich noch 5, nach 11,5 noch 4 usw
...
Ich verstehe die Aufgabe offensichtlich nicht. Der Baustein ließe sich auch recht leicht so umbauen, dass er über Zeit x zum Beispiel eine Stunde sekundengenau die Anzahl der Ereignisse zählt und dann den Wert der letzten zehn Minuten aufaddiert. Das ist dann eine Sekundengenaue Analyse:

Code:
FUNCTION_BLOCK "Analyse2_FB"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT
      Signal : Bool;
   END_VAR

   VAR
      FL_Signal : Bool;
   END_VAR
   VAR RETAIN
      Count : UDInt;
      Gegenwart {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
      Vergangenheit {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
      PerSec : Array[0..3599] of UDInt;
   END_VAR
   VAR
      x : Int;
      y : Int;
      ZehnMinutenCount : UDInt;
   END_VAR

   VAR_TEMP
      RetVal : Int;
      i : Int;
   END_VAR


BEGIN
    //Systemzeit auslesen
    #RetVal := RD_LOC_T(#Gegenwart);
    
    //auf Sekunden in einer Stunde zählen Ringpuffer
    #x := (USINT_TO_INT(#Vergangenheit.SECOND) + USINT_TO_INT(#Vergangenheit.MINUTE) * 60) MOD 3600;
    IF #Gegenwart.SECOND <> #Vergangenheit.SECOND THEN
        #PerSec[#x] := #Count;
        #Count := 0;
    END_IF;
    
    //innerhalb einer Sekunde zählen / Signalauswertung
    IF #Signal AND NOT #FL_Signal THEN
        #Count += 1;
    END_IF;
    #FL_Signal := #Signal; //Flanke
    
    #ZehnMinutenCount := 0;
    FOR #i := 0 TO 599 DO
        #y := #x - #i;;
        IF #y < 0 THEN
            #y += 3600;
        END_IF;
        #ZehnMinutenCount += #PerSec[#y];
    END_FOR;
    
    //Für die Flankenbildung der Zeiten
    #Vergangenheit := #Gegenwart;
    
END_FUNCTION_BLOCK
 
Ich hätte hier mal den FIFO-Baustein aus der LGF genommen und auf deine Bedürfnisse angepasst.
(zumindest, wenn ich es richtig verstanden habe)

"elementCount" wäre dann der Output, der deinen gesuchten Wert ausgibt.

Getestet wurde der Baustein auch noch nicht.

Edit: möglichen (eigentlich sicheren) OoB gefixt
 

Anhänge

  • Trigger_Counter.scl.txt
    12,1 KB · Aufrufe: 7
Zuletzt bearbeitet:
Mir war langweilig und ich hab auch mal einen FB dazu erstellt:
1704877802684.png
Wenn Start aktiv und ein Ereignis auftritt, wird das Ereignis gezählt und in Daten_Aktuell mit Zeitstempel (Systemzeit) abgelegt.
Abhängig vom einstellbaren Zeitraum wird die Anzahl der Ereignisse ausgegeben. Es wird also immer der Zeitraum "Jetzt" bis "Jetzt - X min" betrachtet.
Wenn Start nicht mehr aktiv, werden die Daten von Daten_aktuell in Daten_Senden geschoben und Daten_aktuell geleert.
 

Anhänge

  • FB_Timer_Ereignisse_SCL.zip
    967 Bytes · Aufrufe: 8
Zurück
Oben