Beispiele zur Meldebehandlung
In den folgenden Beispielen wird die kontinuierliche Meldebehandlung mit der diskreten Meldebehandlung verglichen. Im Einzelnen werden Beispielparametrierungen vorgenommen, deren Verläufe, abhängig vom aktuellen Onlinewert, in Diagrammen dargestellt werden.
Beispiel einer analogen Meldebehandlung
In diesem Beispiel wird eine Meldung ausgelöst, wenn der Füllstand eines Tanks zu niedrig ist. Man geht hier davon aus, dass es keine Obergrenze gibt, da der Tank immer mit einer definierten Füllmenge aufgefüllt wird, sobald eine gewisse Untergrenze erreicht wird. Der maximale Füllstand des Tanks beträgt 100 Liter und der minimale 0 Liter.
In der folgenden Abbildung wurde an dem Masterdatenpunktelement _mp_DiskreteAlarme.Int vom Datentyp Integer eine analoge Meldebehandlung mit vier Bereichen parametriert:
Die Grenzwerte wurden folgendermaßen gesetzt:
-
Bereich 4: Gutbereich - 40-MAX_INT Liter
-
Bereich 3: Informationsbereich - 39-21 Liter
-
Bereich 2: Warnbereich - 20-11 Liter
-
Bereich 1: Alarmbereich - 10-MIN_INT Liter
Eine Hysterese von [2,-2] zwischen den Bereichen 4 und 3 bewirkt, dass die Informationsmeldung erst dann ausgegeben wird, wenn ein Füllstand von 38 Liter unterschritten wurde. Ebenso wird die Meldung erst dann ausgeblendet, wenn der Füllstand von 43 Liter überschritten wird. Die Hysterese soll verhindern, dass beim Pendeln im 40er Bereich jedes Mal eine Informationsmeldung ausgegeben wird.
Im folgenden Diagramm erreicht der Füllstand des Tanks die folgenden Zustände:
-
Füllstand sinkt: 100..75..55..45..40..37..20..10
-
Tank wird automatisch aufgefüllt -> beim Auffüllen tritt ein Fehler auf. Füllstand schwankt, da weiterhin kontinuierlich abgefüllt, jedoch mit Unterbrechungen aufgefüllt wird: 11..21..35..45..43..41..37..55..37..20
-
Fehler behoben -> Füllstand steigt wieder bis 100 wieder erreicht ist: 21..30..50..70..90..100
Abkürzungen:
IK - Information KAM
WK - Warnung KAM
WG - Warnung GING
AK - Alarm KAM
AG - Alarm GING
Beispiel einer diskreten Meldebehandlung
Dieses Beispiel beschreibt die Temperatur des Wassers in einem Tank. Das Wasser wird kontinuierlich erhitzt, darf jedoch die Temperatur von 100°C nicht erreichen. Die aktuelle Temperatur des Wassers wird jedes 5 Sekunden ermittelt und ausgegeben.
In der folgenden Abbildung wurde an dem Masterdatenpunktelement _mp_DiskreteAlarme.Int vom Datentyp Integer eine diskrete Meldebehandlung mit vier Bereichen parametriert:
Die Grenzwerte wurden folgendermaßen gesetzt:
-
Bereich 1: Gutbereich -> 20, 40, 60°C
-
Bereich 2: Informationsbereich -> <0, 0-19, 21-39, 41-59, 61-69, >100°C
-
Bereich 3: Warnbereich -> 70-89°C
-
Bereich 4: Alarmbereich -> 90-100°C
Die folgenden Wassertemperaturen (Zustände) werden erreicht:
-
Wasser wird bis zu einer Temperatur von 60°C (optimale Temperatur) erhitzt: 5, 9, 20, 29, 40, 53, 60°C.
-
Automatisierte Messanlage, die den Heizprozess stoppt, fällt aus -> Temperatur wird weiter erhitzt: 65, 75, 85, 92°C
-
Kühlmittel wird hinzugeführt, wodurch die Temperatur rapide sinkt: 90, 75, 60, 50, 40, 25°C
-
Tank mit Kühlmittel leer -> Temperatur steigt wieder: 35, 40, 55, 65, 80°C
-
Fehler an der Messanlage konnte behoben werden -> Temperatur geht auf 60°C zurück (optimal).
Abkürzungen:
IK - Information KAM
IG - Information GING (kein GING-Text bestimmt, daher im Ereignisschirm nicht sichtbar)
WK - Warnung KAM
WG - Warnung GING
AK - Alarm KAM
AG - Alarm GING
Die Grenzwerte werden immer vom höchsten bis zum niedrigsten Bereich nach einer Übereinstimmung überprüft. Das bedeutet, dass in diesem Beispiel zuerst der Alarmbereich geprüft wird. Befindet sich der Wert nicht innerhalb des eingegebenen Wertebereiches bzw. Wertes, so wird der Warnbereich geprüft, usw.. Interessant ist in diesem Beispiel der Informationsbereich. Diese Meldung soll laut Parametrierung immer ausgegeben werden, wenn der Onlinewert ungleich 20, 40 oder 60 ist. Dies bedeutet, dass bei kontinuierlichen Wertänderungen eine Information immer ausgegeben wird, nachdem eine der drei Grenzen überschritten/unterschritten wird, was in einigen Fällen zur besseren Kontrolle sinnvoll sein kann.
Falls es jedoch bereits zu einer Übereinstimmung im Alarmbereich gekommen ist, dann werden die niedrigeren Bereiche nicht mehr geprüft.
Beispiel zum Setzen einer Meldebehandlung für Multiinstanzalarme für CTRL-Funktionsaufrufe
Multiinstanzalarme werden in einem CTRL-Script mit alertSet() gesetzt und mit einer SQL-Abfrage (z.B. dpQuery()) abgefragt. Im Gegensatz zu den anderen Meldebehandlungstypen, wird hier nicht auf die aktuellen Meldebehandlungsattribute zugegriffen (_alert_hdl.._act*), sondern auf das Attribut Meldebehandlung "_alert hdl.<i>._event, welches eine Meldeaktion auslöst, mit der eine Meldung ausgelöst, aktualisiert oder quittiert werden kann. Zusätzlich wird für jeden Multiinstanzalarm von der Peripherie eine eigene Alert-ID auf das Attribut (_alert_hdl.._alert_id) gesetzt. Mit der ID und Alarmzeit kann eine bestimmte Meldung eindeutig identifiziert werden (getrennt für KAM und GING Meldungen eines Meldungspaares verfügbar).
Im folgenden Beispiel wird über alertSet() eine Nachricht an den Event-Manager generiert, der die entsprechenden Attribute des bereits vorhandenen Datenpunktes "MI_Demo" mit parameterierter Multiinstanzmeldebehandlung setzt. Mittels dpQuery() wird anschließend eine SQL-Abfrage durchgeführt, die den Datenpunkt nach Übereinstimmungen der Werte durchsucht. Aus diesen werden dann die Counts mittels getACount() ausgelesen. Wenn der Count bekannt ist, kann auch alertGet() verwendet werden, um z.B. einen Ableich der Multiinstanzalarme auf redundanten Systemen durchzuführen.
main()
{
//Set additional values
dyn_anytype addVal = makeDynAnytype(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32);
time t;
dyn_dyn_anytype attr;
//The current time is stored in a local variable
t = getCurrentTime();
//set an alert on the data point 'MI_Demo.',
//with the alert class 'warning
//with the _add_values we declared above
//and with our alert id
alertSet(t, 0, "MI_Demo.:_alert_hdl.._event", DPATTR_ALERTEVENT_CAME,
t, 0, "MI_Demo.:_alert_hdl.._class", "warning.",
t, 0, "MI_Demo.:_alert_hdl.._add_values", addVal,
t, 0, "MI_Demo.:_alert_hdl.._alert_id", "MI_Demo_id");
//Now we query a config from the data point where the came time and
//the alert id are same as ours
dpQuery("SELECT ALERT '_alert_hdl.._class' FROM 'MI_Demo.' WHERE '_alert_hdl.._alert_id'= \"MI_Demo_id\" AND '_alert_hdl.._came_time' = \"" + formatTime("%Y.%m.%d %H:%M:%S",t, ".%03d") + "\"", attr);
while (dynlen(attr) <= 1)
{
delay(0, 200);
}
DebugTN("ALERT COUNT: " + getACount((atime)attr[2][2]));
//Returns => ALERT COUNT: 0
// => ALERT COUNT: 1 (at next call)
// => ALERT COUNT: 2 (at next call)
// ...
}
The following example sets a multi-instance alert with CAME alert (DPATTR_ALERTEVENT_CAME) and addValues meaning additional values.
int _alertSet(string dpe)
{
time t = getCurrentTime();
/* Add the strsplit to one row. In this example, the example contains a line break for demonstration purposes */
dyn_anytype addValues = strsplit("1c\n2c\n3c\n4c\n5c\n6c\n7c\n8c\n9c\n10c\n11c\n12c\n13c\n14c\n15c\n16c
\n17c\n18c\n19c\n20c\n21c\n22c\n23c\n24c\n25c\n26c\n27c\n28c\n29c\n30c\n31c\n32c",
"\n");
int rc = alertSetWait( t, 0, dpe + ":_alert_hdl.2._event",DPATTR_ALERTEVENT_CAME
,t, 0, dpe + ":_alert_hdl.2._class" , "information."
,t, 0, dpe + ":_alert_hdl.2._add_values", addValues);
}
Das folgende Beispiel setzt einen Multiinstanzalarm mit KAM-Meldung (DPATTR_ALERTEVENT_CAME) und addValues also Alarmbegleitwerten
int _alertSet(string dpe)
{
time t = getCurrentTime();
/* Add the strsplit to one row. In this example, the example contains a line break for demonstration purposes */
dyn_anytype addValues = strsplit("1c\n2c\n3c\n4c\n5c\n6c\n7c\n8c\n9c\n10c\n11c\n12c\n13c\n14c\n15c\n16c
\n17c\n18c\n19c\n20c\n21c\n22c\n23c\n24c\n25c\n26c\n27c\n28c\n29c\n30c\n31c\n32c",
"\n");
int rc = alertSetWait( t, 0, dpe + ":_alert_hdl.2._event",DPATTR_ALERTEVENT_CAME
,t, 0, dpe + ":_alert_hdl.2._class" , "information."
,t, 0, dpe + ":_alert_hdl.2._add_values", addValues);
}
Beispiel zum Gehen lassen eines binären Multiinstanzalarmes
Im folgenden Beispiel werden per dpGet() alle Alarmpaare, d.h. die KAM-Zeit und die GING-Zeit, vom Datenpunkt MI_Demo in einer Variablen abgespeichert. Jede Alarmzeit ist vom Typ atime, dem internen Zeitformat inklusive Count und Datenpunkt-Identifikation. Eine Ausgabe im Log Viewer könnte beispielsweise folgendermaßen aussehen:
[2012.12.22 12:12:12.000 (7 ) System1:MI_Demo. (Type: 41 Sys: 1 Dp: 508 El: 1 : 0..0)]
Die Funktion getACount() liefert den Count zur angegebenen Alarmzeit (der Count würde in diesem Fall 7 sein). Zum Ansprechen von Multiinstanzalarmen sind Alarmzeit und Count unbedingt erforderlich.
Bei den abgespeicherten Alarmzeiten kann mittels der Statusbits (direction, obsolete, visible) überprüft werden, welche davon noch relevante KAM-Meldungen sind und auf GING gesetzt werden sollen.
Hinweis
Dieses Beispiel funktioniert nur bei binären Multiinstanzalarmen. Soll es für Meldebehandlungen analoger Größen verwendet werden, muss das entsprechende Detail angegeben werden. Z.B.: "MI_Demo.:_alert_hdl.2._event" um die Alarme dieses Bereichs anzusprechen (z.B. setzen auf GING). Um also Alarme aller Bereiche zu erhalten bzw. gehen zu lassen, muss jeder einzelne Bereich einzeln angesprochen werden. Siehe das zweite Beispiel unterhalb.
main()
{
dyn_anytype alarm;
bit32 state;
//All alert couples (CAME time & WENT time) of DP MI_Demo are inserted into the
//variable
dpGet("MI_Demo.:_alert_hdl.._alerts", alarm);
//Then all alerts of the _alerts attribute will be checked whether they are
//CAME actions and must be set to WENT.
for(int j = 1; j <=dynlen(alarm); j++)
{
//The status bits of each alert are needed for checking.
int rc = alertGet(alarm[j], getACount(alarm[j]), "MI_Demo.:_alert_hdl.._state", state);
//Shows the pending alerts (alert time, count, status bits)
DebugN("pending alarms:", rc, alarm[j], getACount(alarm[j]), "MI_Demo.:_alert_hdl.._state",
state, state[1], state[4], state[0], state[31]);
//It's checked if the alerts are still valid and if its direction is CAME with
//the status bits "obsolete", "visible" and "direction"
if (rc != 0 || state[1] == 1 /*obsolete*/ || state[4] == 0 /* visible */ || state[0] == 0 /* went */)
continue;
//If the alert fulfills this requirements, the alertSet() function is executed
//and the alert will be set to WENT with the constant DPATTR_ALERTEVENT_WENT
int rc = alertSet(alarm[j], getACount(alarm[j]),"MI_Demo.:_alert_hdl.._event", DPATTR_ALERTEVENT_WENT);
DebugN(rc);
}
}
BEISPIEL
Multiinstanzalarme können über Control ausgelöst werden. Die folgenden zwei Control-Beispiele zeigen wie ein Mulltiinstanzalarm auf KAM und GING gesetzt werden kann.
Im folgenden Beispiel wird ein Mulltiinstanzalarm für den Datenpunkt "ExampleDP_Arg2" ausgelöst. Der Alarm wird über das Attribut "_event" ausgelöst. Das Attribut wird auf "KAM" (CAME) über die Konstante "DPATTR_ALERTEVENT_CAME" gesetzt. Beachten Sie, dass Sie den Alarmbereich für die Meldebehandlung angeben müssen. Bei einem Multiinstanzalarm mit einem Gutbereich und zwei Meldeklassen (Grenzwerten) - Warnung und Alarm, wären die Meldeklassen (Schlechtbereiche) die Bereiche 2 (Warnung) und 3 (Alarm). Im folgenden Beispiel wird eine Warnung (Bereich 2) durch "_alert_hdl.2._event" ausgelöst. Wenn ein Alarm ausgelöst werden soll, muss der Bereich 3 angegeben werden ("_alert_hdl.3._event").
main()
{
int retVal;
time t = makeTime(2016,04,20,15,44,00,000);
retVal = alertSet(t,0, "System1:ExampleDP_Arg2.:_alert_hdl.2._event",DPATTR_ALERTEVENT_CAME);
/* Note that when you want to set an alert to WENT, the time must correspond to the
CAME time and you have to specify the exact time including the milliseconds. In addition,
the ID (meaning the alert count)of the alert must correspond to the count of the
CAME alert. In this example the alert count is 0. See the second parameter in the
alertSet function call*/
DebugN("RetVal:", retVal);
}
Um einen Alarm auf GING zu setzen, verwenden Sie die Konstante "DPATTR_ALERTEVENT_WENT"
main()
{
int retVal;
time t = makeTime(2016,04,20,15,44,00,000);
retVal = alertSet(t,0, "System1:ExampleDP_Arg2.:_alert_hdl.2._event",DPATTR_ALERTEVENT_WENT);
DebugN("RetVal:", retVal);
}
Sie können die Alarmzeit z.B. über das Attribut _alert_hdl.._alerts abfragen. Das Attribut liefert Alarmpaare. Die Alarm-ID (Count) kann über die Funktion getACount() abgefragt werden.
main()
{
dyn_anytype alert;
bit32 state;
//All alert pairs are written to the variable "alert"
dpGet("System1:ExampleDP_Arg2.:_alert_hdl.._alerts", alert);
DebugN("dpGet of the alert:", alert);
//Outputs all alerts
for(int j = 1; j<=dynlen(alert); j++)
{
/* The attribute _alert_hdl.._state contains the status bits for an alert in a bit32.
getACount returns the count that is needed for the alertSet, see example above */
int rc = alertGet(alert[j], getACount(alert[j]),"ExampleDP_Arg2.:_alert_hdl.._state",state);
//Outputs the pending alerts (alert time, count)
DebugN("Pending alerts:", rc, alert[j],"getAcount:", getACount(alert[j]));
}
}
Der Code-Output:
WCCOAui1:["dpGet of the alert:"][dyn_anytype 2 items
WCCOAui1:
1: 2016.04.20 16:25:40.591 (0 ) :_alert_hdl.3 (Type: 0 Sys: 0 Dp: 0 El: 0 : _alert_hdl.3.0)
WCCOAui1:
2: 2016.04.20 16:27:59.869 (1 ) :_alert_hdl.3 (Type: 0 Sys: 0 Dp: 0 El: 0 : _alert_hdl.3.0)
WCCOAui1:
]
WCCOAui1:
["Pending alerts:"][0][2016.04.20 16:25:40.591 (0 ) :_alert_hdl.3
(Type: 0 Sys: 0 Dp: 0 El: 0 : _alert_hdl.3.0)]["getAcount:"][0]
WCCOAui1:
["Pending alerts:"][0][2016.04.20 16:27:59.869 (1 ) :_alert_hdl.3
(Type: 0 Sys: 0 Dp: 0 El: 0 : _alert_hdl.3.0)]["getAcount:"][1]
BEISPIEL
Das folgende Beispiel setzt einen Multiinstanzalarm mit KAM-Meldung (DPATTR_ALERTEVENT_CAME), Meldeklasse "Information" und addValues also Alarmbegleitwerten.
-
main() { int retVal; time t = makeTime(2019,05,13,9,54,00,000); retVal = alertSet(t,0, "System1:ExampleDP_Arg1.:_alert_hdl.2._event",DPATTR_ALERTEVENT_CAME); /* Note that when you want to set the alert to WENT, the time must correspond to the CAME time (including milliseconds) In addition, the ID of the alert, meaning the alert count must correspond to the count of the CAME alert. In this example the alert count is 0. See the second parameter in the alertSet function call */ DebugN("RetVal:", retVal); dyn_anytype addValues = strsplit("1c\n2c\n3c\n4c\n5c\n6c\n7c\n8c\n9c\n10c\n11c\n12c\n13c\n14c\n15c\n16c\n17c\n18c\n19c\n20c\n21c\n22c\n23c\n24c\n25c\n26c\n27c\n28c\n29c\n30c\n31c\n32c","\n"); int rc = alertSet( t, 0, "System1:ExampleDP_Arg1.:_alert_hdl.2._event",DPATTR_ALERTEVENT_CAME ,t, 0, "System1:ExampleDP_Arg1.:_alert_hdl.2._class", "information." ,t, 0, "System1:ExampleDP_Arg1.:_alert_hdl.2._add_values",addValues); }
Beispiel zum Quittieren eines Multiinstanzalarms über einen Treiber
Externe Alarme der Peripherie (S7, BACnet, OPC) die vom Treiber quittiert werden sollen, werden auf die internen Datenpunktelemente _Driver.AL.AckDps und _Driver.AL.AckData geschrieben (siehe auch Multiinstanzalarme für weitere Informationen). Das geschieht entweder durch Quittieren im Alarmschirm oder die Funktion isAckable(). Folgendes Beispiel zeigt wie der Funktion isAckable() ein zu quittierende Alarm übergeben werden kann.
main()
{
int ret;
//time, count and DPE must correspond to an unacknowledged alert
//The attribute of the DPE (in our case _abbr) does not matter
time t = makeTime(2016,4,12,14,30,23, 608, true);
atime a = makeATime(t, 14, "System1:ClientAlarm_2.boolean:_alert_hdl.2._abbr");
dyn_atime dps = makeDynATime(a);
isAckable(2, dps, ret);
DebugN(ret);
}