Hallo,
nachfolgend als Ergebnis der Orkan-Emma-Vorsorge des letzten Wochenendes (endlich mal USV in Betrieb genommen, falls Strom wieder ausfällt…), ein Script, dass das Datenlog einer USV (im konkreten Beispiel: Smart-UPS von APC) in IPS-Variablen einliest.
Ergänzung: (APC-)USVs schreiben zwei Logs: Ein Datenlog und ein Eventlog. Dieser Artikel beinhaltet das Einlesen des Datenlogs. Hier dagegen ein Beispielscript zum Einlesen des Eventlogs der USV
Die Steuersoftware von APC heißt „Powerchute“ und kann in der Basic-Version von APC.com kostenfrei nach Registrierung runtergeladen werden. Aktuelle Version: 7.0.5 Build 108. Es gibt auch ein Patch für Vista, ansonsten ist die Basic-Version im LAN auch remote betreibbar (getrennte Agent-Server-Client-Architektur, auch mehrere USV-gepufferte Maschinen=Agents mit einem Powerchute-Server managebar)
Das Powerchute selbst, das natürlich erstmal als Hauptaufgabe das „geordnete Herunterfahren, falls Batterie dann auch leer ist“ usw. macht, legt zyklisch Daten-Records in ein Log auf den Server. Im Programm kann dazu u.a. der Bereinigungszyklus sowie die „Dichte der Signalaufzeichnung“ eingestellt werden. (Zitat Help-Datei: „From a minimum of 10 seconds, through a maximum of 59 minutes (20 minutes is the default).“
Was sind das für Daten:
Die APC-USVs haben bereits einen internen Temperatursensor (per Offset: Aussage zur umgebenden Raumtemp.). Weiterhin werden pro Record z.B. aufgezeichnet: anliegende Spannung, gelieferte Spannung, Batteriespannung, „Load“ also Last an der USV, Netzfrequenz usw. Ist natürlich alles für Serverraumüberwachung usw. gedacht, aber hier auch ganz nützlich, besonders wenn der IPS-Server „irgendwo klimarelevant untergebracht“ stehen sollte. …und man sowieso schon so eine USV zum Abfedern von Stromausfällen bzw. -aussetzern und -schwankungen in Betrieb hat.
Schließt man optionale APC-Sensoren an die USV an, kann man sogar Daten zur Feuchtigkeit und weitere externe Temp-Werte bekommen, die ebenfalls in dieses Log einfließen.
Mein Ziel ist vor allem, Aussagen zur Qualität der gelieferten Spannung zu bekommen. Z.B. ist es im regionalen Umfeld in letzter Zeit üblich geworden, statt 230V insbesonders nachts regelmäßig >240V zu liefern. Wenn man bedenkt, das die Rotation der Zähler-Scheibe spannungsabhängig zunimmt… Ich möchte (erstmal) zumindest ein Auge drauf haben können, wo sich IPS und WIIPS natürlich automatisch anbieten.
Das Log wird im folgenden Script zyklisch ausgelesen sowie recordweise (=alle Werte einer Zeile im Log) in Variablen gelegt. Die 4-Sekunden-Verzögerung („sleep(4)“) am Ende der Schleife bewirkt, dass bei evtl. mehreren zwischenzeitlich als „neu“ aufgelaufenen und zu verarbeitenden Records dazwischen genügend Zeit bleibt, um diese durch andere Scripts weiter zu verarbeiten (z.B. per „OnUpdate“ getriggert in Datenbank eintragen o.ä.)
Eine Besonderheit ist die Variable „UPS.Datalog.lastTs“. Diese hält den Zeitstempel (=Timestamp), bezogen auf die Record-Logzeit der letzten erfolgreich eingelesenen Log-Zeile. Also wenn die letzte erfolgreich gelesene Zeile den Zeitstempel „01.03.2008 10:38:15“ hatte, werden im nächsten Aufruf des Scripts nur nach darauf bezogen neueren Log-Zeilen gesucht und diese eingelesen.
Damit ist das Script derart robust, dass selbst ein Abbruch wegen maximaler Script-Laufzeit von 150(?) Sekunden nichts ausmacht. Im nächsten Zyklus macht es einfach beim nächsten Log-Record weiter.
Meine Funktion „SetValue()“ ist übrigens eine Vereinfachung bzgl. der unterschiedlichen Typen (Int, Boolean, Float, String), die darin automatisch erkannt werden. Außerdem werden auch gleich, wenn noch nicht vorhanden, passende Variablen im IPS anzulegen. Wenn ich Paresy richtig verstanden habe auf dem IPS-Together, ist das ja bei V2.0 dann ohnehin so.
„SetValue()“ steht einen Beitrag tiefer!
Wer das nicht hat/will: einfach aus „SetValue()“ dann „SetValueFloat()“ oder „SetValueInteger()“ machen.
Ebenfalls in der Include-Datei „_globals.ips.php“:
- semaset() und semareset() = Semaphoren-Handling
- Bildung der Variable „$myself“ = Scriptname
Wer es nicht braucht: einfach auskommentieren.
Viel Spass damit
Gerd
<?
/*
*******************************
IP-SYMCON Event Scripting
*******************************
File : readApcLog.ips.php
Trigger : ---
Interval : Zyklus wie Zeitabstand UPS-Data-Log (Default 20 min = 1200 Sec)
APC Powerchute Data Log einlesen
Rel. 1 GW 01.03.2008
*/
include "_globals.ips.php";
semaset($myself, 10000);
if (!IPS_VariableExists("UPS.Datalog.Path")) { // wenn Variable fehlt: anlegen
IPS_CreateVariable("UPS.Datalog.Path", "String");
SetValueString("UPS.Datalog.Path", "c:\Programme\APC\PowerChute Business Edition\agent\DataLog");
}
$datei = GetValueString("UPS.Datalog.Path");
if (!IPS_VariableExists("UPS.Datalog.lastTs")) { // wenn Variable fehlt: anlegen
IPS_CreateVariable("UPS.Datalog.lastTs", "Integer");
SetValueInteger("UPS.Datalog.lastTs", 0);
}
$lastTs = GetValueInteger("UPS.Datalog.lastTs");
$zTs = 0;
if (file_exists ($datei)) {
$zeilen = liesFile($datei);
foreach ($zeilen as $zeile) {
$zeile = preg_replace("/\s+/", "|", $zeile);
// echo $zeile."
";
$dat = explode("|", $zeile);
if (sizeof($dat) > 9) {
if ((strpos($dat[0], "/") === 2) and (strpos($dat[1], ":") === 2)) {
list($monat, $tag, $jahr) = explode("/", $dat[0]);
list($stunde, $minute, $sekunde) = explode(":", $dat[1]);
$zTs = mktime($stunde, $minute, $sekunde, $monat, $tag, $jahr);
// echo $zTs."
";
if ($zTs > $lastTs) {
// echo makeFloat($dat[2])."
";
SetValue("UPS.Data.MaxLineVoltage", makeFloat($dat[2]));
SetValue("UPS.Data.MinLineVoltage", makeFloat($dat[3]));
SetValue("UPS.Data.LineVoltage", makeFloat($dat[4]));
SetValue("UPS.Data.OutputVoltage", makeFloat($dat[5]));
SetValue("UPS.Data.BatteryVoltage", makeFloat($dat[6]));
SetValue("UPS.Data.OutputFrequency", makeFloat($dat[7]));
SetValue("UPS.Data.UpsLoad", makeFloat($dat[8]));
SetValue("UPS.Data.UpsInternalTemperature", makeFloat($dat[9]));
SetValue("UPS.Data.Ts", $zTs);
SetValue("UPS.Data.Time", date("d.m.Y", $zTs)." ".date("H:i:s", $zTs));
SetValue("UPS.Datalog.lastTs", $zTs);
sleep(4);
}
}
}
}
}
semareset($myself);
function makeFloat($wert) {
return floatval(preg_replace("/,/", ".", $wert));
}
?>