Störungen gezielt loggen?

McNugget

Level-1
Beiträge
220
Reaktionspunkte
10
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo allerseits, habe mal wieder eine kleine Nuss, die es zu knacken gilt. (passend zu Weihnachten, hehe...)

Ich werte in meinem Wago 750-841 ca. 50 digitale Störmeldungen (TRUE = Fehler) aus, die mir eine Anlage von Run in Stop setzen.

Dazu verwende ich den Baustein ONTIME aus der OSCAT.LIB.

Dieser schreibt die Häufigkeit und die Dauer des jeweiligen Eingangs mit, so lange der Enable-Eingang True ist/wird.

Wie bekomme ich es nun hin, dass ich das mit dem fallenden Run-Signal der Anlage (Run = True = Anlage läuft) verknüpfe?

Beispiel:
Ich möchte nicht einen Not-Aus mitschreiben, der bei stehender Anlage geschlagen wird, oder einen Motorschutzschalter, der bei stehender Anlage auslöst.

Aber wenn die Anlage läuft und ein ausgelöster Not-Aus oder eine abfallender Motorschutzschalter sorgt für einen Stop aus laufender Anlage, dann möchte ich das mitschreiben.



Wie mache ich das, dass ich nur die für den Stop der Anlage ursächlichen Eingang so lange mitschreibe, wie er ansteht?
 
Hallo,
ich unterstelle mal, das Stop nicht Stop der CPU sondern Stop des Ablaufs ist ...
In dem Fall hättest du ja einen "Merker" mit der Funktion "Anlage läuft". Diesen bräuchtest du dann m.E. nur in Verbindung mit deinem Fehler auswerten - das heißt : nur dann den Zustand mit-loggen, wenn Abschaltbedingung kommt und Anlage ist noch im "Run". Damit sperrst du dann auch automatisch jeden weiteren Auslöser ...

Gruß
LL
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Korrekt, im Moment gehe ich direkt mit dem DI der Steuerung auf den Enable Eingang von ONTIME.

Aber der loggt nur, so lange ein Signal anliegt.

Wie nehme ich die fallende Flanke von Run (ja klar Anlagen-Run und nicht CPU) als Auslöseereignis und behalte dann einen High Eingang am ONTIME??

Ich stehe da etwas auf´m Schlauch...
 
... vielleicht in etwa so :
Code:
if not Anlage_aktiv and Last_State_Anlage_aktiv then
   if Ereignis_1 then Merker_Ereignis_1 := true ;
   elsif Ereignis_2 then Merker_Ereignis_2 := true ;
   ... // hier stehen alle weiteren Bedingungen ...
   end_if 
end_if ;
Last_State_Anlage_aktiv := Anlage_aktiv ;

if Anlage_aktiv then
   Merker_Ereignis_1 := false ;
   Merker_Ereignis_2 := false ;
end_if ;
Kannst du damit (Q'n'D) etwas anfangen ?
 
Hallo Larry, habe es in FUP gemacht.

Dein Ansatz mit dem Merker hat mich etwas aufgeweckt. (Kopf ist wohl schon unter´m Tannenbaum.)

Ich habe es jetzt mal so gelöst: (Siehe Anhang).

Was denkst Du darüber?

Ich müsste jetzt ja nur noch den Ausgang von RS_01 in jedem weiteren Netzwerk vor´s AND setzen, oder? (%IX4.13 ist die Run-Meldung der Anlage)

Ginge das eleganter, oder ist das so ok?
 

Anhänge

  • Anlage.pdf
    19,6 KB · Aufrufe: 43
Zuviel Werbung?
-> Hier kostenlos registrieren
...in diesem Zusammenhang noch mal:


Wie könnte man aus den 50 Einzelwerten für Ausfallanzahl eine Top-Ten in der Visu generieren, die sich Absteigend je nach Ausfallhäufigkeit aktualisiert?

Das gleiche natürlich für die Ausfalldauer.

Sehe ich das richtig, dass ich diese 50 Auswertungen besser in ein Array schreiben sollte, welches ich dann einmal nach der Spalte der Ausfallzyklen und dann nach der Gesamtdauer sortiere?

Wie würde das in ST aussehen? kann ich die Eingänge der Steuerung in einer Schleife hochzählen lassen?

Fragen über Fragen......
 
Zuletzt bearbeitet:
Hab´s jetzt mal so versucht und läuft nicht:

Deklaration:
PROGRAM Stoerlog_ST

VAR_INPUT
IN_1:BOOL; (*Störmeldeeingang Schliesser*)
Reset: BOOL;
RUN:BOOL; (*Run-Meldung der Abtriebe*)
END_VAR

VAR_OUTPUT
Stoerungen: UDINT;
Stoerdauer: TIME;

END_VAR

VAR
Last_State_was_run: BOOL;
Ontime_01:ONTIME;
Seconds: UDINT;
Cycles:UDINT;
Merker_Ereignis_1: BOOL;

END_VAR

Anweisungsteil:
IF NOT RUN AND Last_State_was_RUN THEN Merker_Ereignis_1 := TRUE ;

ONTIME_01(IN:=Merker_Ereignis_1, seconds:=seconds, cycles:=cycles, rst:=reset);
Stoerdauer:= SECOND_TO_TIME(SECONDS);


END_IF ;


Last_State_was_run := RUN ;

IF RUN THEN Ontime_01.IN := FALSE ;
END_IF ;

IF Reset THEN Ontime_01.RST :=TRUE ;

END_IF;

Was ist denn so falsch daran?
 
Hallo,
ich beziehe mich mal auf dein Script des Beitrages #7.
Wenn du den Aufruf der Ontime-Funktion ausserhalb des IF..THEN machst, dann sollte das funktionieren. Also in etwa so :
Code:
IF NOT RUN AND Last_State_was_RUN THEN Merker_Ereignis_1 := TRUE ;
END_IF ;

ONTIME_01(IN:=Merker_Ereignis_1, seconds:=seconds, cycles:=cycles, rst:=reset);
 Stoerdauer:= SECOND_TO_TIME(SECONDS);

... des weiteren ...
Wenn du mit sehr vielen Meldungen und Ereignissen arbeiten möchtest, dann empfiehlt sich hier ein ARRAY - klar ...
Ich würde hoier dann allerdings NICHT mit 50 (oder mehr) Aufrufen des Ontime arbeiten sondern mit da selber etwas zusammenbauen - so a la Fehler kommt, dann Uhrzeit merken, Fehler geht dann akt. Uhrzeit - gespeicherter Uhrzeit = Fehler-Dauer.

Die Fehler-Zeiten könntest du hinterher z.B. mit Bubblesort (dies mal als Schlüsselwort in die Suche eingeben) auf- oder absteigend sortieren. Hierbei könntest du dann auch einen Fehler-Schlüssel mit-sortieren ...

Komst du damit erstmal weiter ?

Gruß
LL
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

deine PDF ist verwirrend.

Warum machst du es so kompliziert mit der Negativen Flanke und dem RS?

Mach doch einfach

Eingang_Der_Überwacht_wird (Negiert) UND
Eingang_Anlage_Run

Auf dem Baustein.

Das ist aber nur eine kleine Codeverfeinerung... das mit dem Sortieren würde ich persönlich in nem Array machen, da rechnet es sich schneller.

MfG

Marcel
 
Guten Morgen.

@Matze: So wie Du es vorschlägst, hat es nicht mehr die Funktion, die ich bräuchte. Aber Recht hast Du, ich habe es hakelig gemacht.

Ich muss loggen, wenn Eingang_Anlage_Run FALSE wird UND ein beliebiger Störmeldeeingang TRUE wird.

(Sprich: welche Störmeldung führte zum Stillstand.)

Ich weiss, dass das wie ich es im FUP gelöst habe, auch nicht optimal ist, aber es erfüllt erst mal die primitivsten Anforderungen.

Ich bin leider immer noch hin und hergerissen zwischen FUP, mit dem man zwar vieles quick and dirty lösen kann, aber eben nicht elegant und ST, was mich noch Unmengen an Gehirnschmalz kostet.


@Larry: Danke. Wenn das mit der ersten IF-Bedingung so liest, ist es klar.
Ich mache es im Moment in der Tat mit über 50 Ontime-Aufrufen und weiss, dass das alles andere als schlank und effizient ist.

Habe mir ein Array of Struct gemacht und in die Struktur schreibe ich Bezeichnung: Dauer; Fehlerhäufigkeit.

Bubblesort habe ich mir schon mal vor einiger Zeit im WIKI angeschaut. Interessant ist es.
Aber könnte es nicht eleganter sein, anhand der Struktur nach Spalte 2 oder 3 (je nachdem, was ich finden will) die Top-Ten, der Störungen zu generieren?

Ich schaue mal, wie ich es mit einem selbst geschriebenem Multi-Ontime-Baustein hinbekomme.

Oder kann ich vielleicht in eicher Schleife x-mal den Ontime-Abustein auftrufen und jeweils wieder mit neuen Parametern belegen?


Dazu aber mal ein paar Fragen: Kann ich die physikalischen Eingänge des Wago-Controllers in einer Schleife hochzählen, um sie zu überwachen, oder geht das auch z. B. in einem Array, oder muss ich mit 55 Eingängen arbeiten?
(Bin doch faul und will mir eintönige Vieltipperei ersparen. ;-) )

Unglücklicherweise sitzen zwischen den Störmeldungen auch noch einige andere Sensorsignale (z. B. Anlage_Run).
Kann ich bei einer Schleife, mit der ich hochzähle, einzelne Ziffern überspringen/ignorieren, oder muss ich erst eine neue Zuweisung ohne Lücken erstellen, bzw. nach dem Schreiben des Arrays die unwesentlichen Elemente ignorieren?
 
Guten Morgen zurück ...

Ich habe mir zu deinem Problem gerade auch mal ein paar Gedanken gemacht.
Ich würde die "Stör-Eingänge" wahrscheinlich als ARRAY of BOOL an den Baustein übergeben (und dann halt dem entsprechend von außen dann vorher zuweisen - das macht zwar auch arbeit, etwas eleganteres fällt mir da im Augenblick aber nicht ein).
Auf diesem Weg könntest du dann eine Auswertung in einer Schleife machen.
Wenn du den gleichen Ontime 50 mal benutzen willst, so mußt du mit 50 verschiedenen Instanzen des Bausteins arbeiten. Das macht deinen Programmcode betsimmt nicht schlanker - deshalb von mir der Vorschlag, das was der Baustein macht selber zu erstellen. M.E. ist das nicht soviel Arbeit. Du mußt dir dann halt nur für jeden Fehler merken, ob er gerade aktiv ist, wenn er aktiv ist, wann er begonnen hat und natürlich, wie lange er vorher schon mal aktiv war. Hier kannst du dir dann die Dauer dann auch schon gleich in Sekunden (oder ähnlich) ausrechnen.

Bubblesort (dazu gibt es hier auch im Forum Einiges) kannst du natürlich unterschiedlich betreiben. Es sind dann halt nur unterschiedliche Durchläufe bzw. Programm-Abschnitte.

Gruß
LL
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Guten Morgen.

@Matze: So wie Du es vorschlägst, hat es nicht mehr die Funktion, die ich bräuchte. Aber Recht hast Du, ich habe es hakelig gemacht.

Ich muss loggen, wenn Eingang_Anlage_Run FALSE wird UND ein beliebiger Störmeldeeingang TRUE wird.

(Sprich: welche Störmeldung führte zum Stillstand.)

Hallo,

so wie es geschrieben ist reicht aber wirklich der Negierte Eingang. Teste es mal. Weil du hast dir die Funktion gebaut:

Wenn der Eingang von 1 auf 0 Wechselt wird ein Merker TRUE, wenn der Merker wieder kommt wird er False... genau das macht das Negieren doch auch...

Das erfassen des erstens machste ja mit dem Zustand Run.

Ich schreib dir nen Programmbeispiel:

MfG

Marcel
 

Anhänge

  • vorschlag.png
    vorschlag.png
    9,4 KB · Aufrufe: 19
@Matze: Jep. Du hast recht. Blind vor Bäumen.



So, nun habe ich ein Struct gebaut.

TYPE Stoerungen_arr :
STRUCT


Stoerkanal: STRING;
Dauer: TIME;
Zyklen: UDINT;

END_STRUCT
END_TYPE
Dieses will ich nun folgendermassen füllen.

FUNCTION_BLOCK Stoerbit_Array
VAR_INPUT

reset: BOOL;
END_VAR

VAR_OUTPUT

END_VAR

VAR


I: INT;
SStoer: BOOL;
Stoerung:BOOL;

Stoermerker: BOOL;
i_Stoerung: INT;
Ontime_A:ONTIME;
Merker_Ereignis_1: BOOL;
Seconds: UDINT;
cycles: UDINT;
Ontime_ZEit: TIME;
END_VAR
b_stoerbits[1] := X25_01 OR TEST_X25_01 ;
b_stoerbits[2] := X25_02 OR TEST_X25_02 ;
b_stoerbits[3] := X25_03 OR TEST_X25_03 ;
b_stoerbits[4] := X25_04 OR TEST_X25_04 ;
b_stoerbits[5] := X25_05 OR TEST_X25_05 ;
b_stoerbits[6] := X25_06 ;
b_stoerbits[7] := X25_07 ;
b_stoerbits[8] := X25_08 ;
b_stoerbits[9] := X25_09 ;
b_stoerbits[10] := X25_10 ;
b_stoerbits[11] := X25_11 ;
b_stoerbits[12] := FALSE ;
b_stoerbits[13] := FALSE ;
b_stoerbits[14] := FALSE ;
b_stoerbits[15] := X25_15 ;
b_stoerbits[16] := X25_16 ;
b_stoerbits[17] := X25_17 ;
b_stoerbits[18] := X25_18 ;
b_stoerbits[19] := X25_19 ;
b_stoerbits[20] := X25_20 ;
b_stoerbits[21] := X25_21 ;
b_stoerbits[22] := X25_22 ;
b_stoerbits[23] := X25_23 ;
b_stoerbits[24] := X25_24 ;
b_stoerbits[25] := X25_25 ;
b_stoerbits[26] := X25_26 ;
b_stoerbits[27] := X25_27 ;
b_stoerbits[28] := X25_28 ;
b_stoerbits[29] := X25_29 ;
b_stoerbits[30] := X25_30 ;
b_stoerbits[31] := X25_31 ;
b_stoerbits[32] := X25_32 ;
b_stoerbits[33] := X25_33 ;
b_stoerbits[34] := X25_34 ;
b_stoerbits[35] := X25_35 ;
b_stoerbits[36] := X25_36 ;
b_stoerbits[37] := X25_37 ;
b_stoerbits[38] := X25_38 ;
b_stoerbits[39] := X25_39 ;
b_stoerbits[40] := X25_40 ;
b_stoerbits[41] := X25_41 ;
b_stoerbits[42] := X25_42 ;
b_stoerbits[43] := X25_43 ;
b_stoerbits[44] := X25_44 ;
b_stoerbits[45] := X25_45 ;
b_stoerbits[46] := X25_46 ;
b_stoerbits[47] := X25_47 ;
b_stoerbits[48] := X25_48 ;
b_stoerbits[49] := X25_49 ;
b_stoerbits[50] := X25_50 ;
b_stoerbits[51] := X25_51 ;
b_stoerbits[52] := X25_52 ;
b_stoerbits[53] := X25_53 ;
b_stoerbits[54] := X25_54 ;
b_stoerbits[55] := X25_55 ;




FOR I:=0 TO 55 DO
SStoer:=b_Stoerbits;
IF SSToer AND X25_14 THEN
i_Stoerung:=i;
Merker_Ereignis_1;
ONTIME_A(IN:=Merker_Ereignis_1, seconds:=Seconds, cycles:=cycles, rst:=reset);
Ontime_Zeit := SECOND_TO_TIME(Seconds);

IF X25_14 AND Merker_Ereignis_1 THEN
Daten.Dauer:=Daten.Dauer + Ontime_Zeit;
Ontime_A.RST := TRUE;
Daten.Zyklen:= Daten.Zyklen + 1;
Merker_Ereignis_1 :=0;
END_IF


END_IF

END_FOR

Habe der Bequemlichkeit halber wieder mit ONTIME gearbeitet (den werde ich auch noch knacken und nur das nutzen, was ich brauche..) Diesen rufe ich zum loggen auf und nachdem die Einschaltbedingun wegfällt, soll erst die Zeit weggeschrieben /addiert. werden und dann soll ONTIME zurückgesetzt werden.

"Selbstverständlich" läuft das nicht so, wie ich es will.

Es muss doch möglich sein, die neue Zeit des aktuellen Stillstandes der Zeit in der Struct zuzuaddieren. Gleiches mit der Anzahl der Störzyklen.

Mir reicht es, jeweils die Störung zu loggen, die für den Stillstand verantwortlich war. (also immer die erste. Wenn weitere in der Störzeit hinzukommen, ist mir das egal.


Hat jemand einen Tip für mich?


ST und Arrays hassen mich und ich hasse sie dafür !!!
:sw10: RACHÄÄÄÄÄÄ...
(nicht ganz ernst gemeint.. aber es immt mir etwas den Frust..)
 
Zuletzt bearbeitet:
... ist schon lustig ... :rolleyes: ... du machst den selben Fehler gerne wieder ...
Code:
FOR I:=0 TO 55 DO
   SStoer:=b_Stoerbits[i];
        IF SSToer AND X25_14  THEN
           i_Stoerung:=i;
           Merker_Ereignis_1 [COLOR=Red]:= true [/COLOR];
      [COLOR=Red] exit ;[/COLOR]
   end_if ;
end_for ;

    ONTIME_A(IN:=Merker_Ereignis_1, seconds:=Seconds, cycles:=cycles, rst:=reset);
    Ontime_Zeit := SECOND_TO_TIME(Seconds);

        IF X25_14 AND Merker_Ereignis_1 THEN
           Daten[[COLOR=Red]i_Stoerung[/COLOR]].Dauer:=Daten[[COLOR=Red]i_Stoerung[/COLOR]].Dauer + Ontime_Zeit;
            Ontime_A.RST := TRUE;
            Daten[[COLOR=Red]i_Stoerung[/COLOR]].Zyklen:= Daten[[COLOR=Red]i_Stoerung[/COLOR]].Zyklen + 1;
            Merker_Ereignis_1 :=false;
        END_IF
... das hätte schon eher Chancen zu funktionieren ... richtig zufrieden bin ich damit allerdings auch nicht.

Was ist "X25_14" ?

Nach meiner Meinung -unabhängig ob es nur ein Ereignis gleichzeitig geben kann- würde ich das Script immer versuchen, so aufzubauen, dass es mit mehreren gleichzeitigen Ereignissen auch arbeiten kann (so etwas soll es ja auch geben ...).

Wenn du mit ARRAY's arbeitest, dann stell dir doch einfach mal einen Setzkasten vor - in diesem Fall einen ein-dimensionalen ...

Für die Arbeitsweise des FB's :
Ein IN-Parameter müßte m.E. die Betriebsart sein - merken mußt du dir dann (quasi als Flanke) welches die letzte ausgewertete Betriebsart war. Dann kann der FB seine Entscheidungen fast selbst treffen.

Gruß
LL
 
AARGH.. Klar.. Ontime-Aufruf aus der Schleife raus... :icon_redface:

Sorry... (leider fehlt hier der Smiley, der sich vor die Stirnschlägt...)

Ähh, was machst Du mit dem Exit? Was bezweckt es da und auf welche IF-Bedingung bezieht es sich?


Cool.

Gerade war ich auch auf [i_Stoerung] gekommen. so weit war ich schon mal.

Konnte nicht sauber testen, da ein Schlosser Teile der Anlage abgeschaltet hatte und ich es nicht bemerkt habe, ---> räumliche Distanz >100m.

X25_12 bis X25_14 sind Statusmeldungen die KEINE Störungen sind. Irgendwer hat die da mal mit in die Klemmleiste mit reinverdrahtet.

Ich habe es so übernommen aber in meinen beiden Arrays sollen die IMMER als FALSE dastehen. Daher her die exotische Zuweisung.
(finde es selber "unglücklich".)

Wie gesagt, ich fange ja erst an mit dem Thema, daher bin ich ja schon mal glücklich, wenn ich erst mal eine Prozedur habe, die mir nur die erste Störung wegloggt.

Klar, die anderen würde mich auch interessieren, aber die wichtigste ist immer die, die für Stillstand sorgt.

Später werde ich auch noch Störungen loggen wollen, die das Einschalten der Anlage verhindert haben.

Wenn ich diese Infos dann auswerte, kann ich Aussagen über die Qualität/Störanfälligkeit der Anlagenteile machen.
 
Ähh, was machst Du mit dem Exit? Was bezweckt es da und auf welche IF-Bedingung bezieht es sich?

Der Exit bezieht sich auf die Schleife - du hast den ersten Eintrag gefunden und kannst diese dann abbrechen. Das macht der Exit.

Den Durchlauf der Schleife solltest du nur ermöglichen, wenn noch kein Fehler erkannt wurde. Alles andere bringt nur Fehl-Auswertungen - du willst ja auch nur nach einem Fehler suchen, wenn du noch nichts hast ...

Gruß
LL
 
Zurück
Oben