new upstream 2.8.0

This commit is contained in:
lagertonne 2022-06-29 12:37:36 +02:00
parent fc7f4b43c1
commit b2b0c9995a
836 changed files with 137090 additions and 30018 deletions

View file

@ -2,7 +2,8 @@
Copyright (C)
2008-2009 Arjen de Korte <adkorte-guest@alioth.debian.org>
2009 Arnaud Quette <ArnaudQuette@Eaton.com>
2009-2021 Eaton (author: Arnaud Quette <ArnaudQuette@Eaton.com>)
2017 Eaton (author: Jim Klimov <EvgenyKlimov@Eaton.com>)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,6 +20,8 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h" /* must be the first header */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -30,18 +33,29 @@
#include "netxml-ups.h"
#include "mge-xml.h"
#include "main.h" /* for testvar() */
#define MGE_XML_VERSION "MGEXML/0.36"
#define MGE_XML_VERSION "MGEXML/0.25"
#define MGE_XML_INITUPS "/"
#define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml"
#define ST_FLAG_RW 0x0001
#define ST_FLAG_STATIC 0x0002
extern int shutdown_duration;
static int mge_ambient_value = 0;
/* The number of phases is not present in XML data as a separate node,
* but we can infer it from presence of non-zero data on several
* per-line nodes. */
static int
inited_phaseinfo_in = 0,
inited_phaseinfo_bypass = 0,
inited_phaseinfo_out = 0,
num_inphases = -1,
num_bypassphases = -1,
num_outphases = -1;
static char mge_scratch_buf[256];
static char var[128];
@ -49,6 +63,9 @@ static char val[128];
static int mge_shutdown_pending = 0;
/* This flag flips to 0 when/if we post the detailed deprecation message */
static int mge_report_deprecation__convert_deci = 1;
typedef enum {
ROOTPARENT = NE_XML_STATEROOT,
@ -114,9 +131,9 @@ typedef struct {
NULL if no further processing is required) */
} xml_info_t;
static const char *online_info(const char *val)
static const char *online_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(ONLINE);
} else {
STATUS_CLR(ONLINE);
@ -125,9 +142,9 @@ static const char *online_info(const char *val)
return NULL;
}
static const char *discharging_info(const char *val)
static const char *discharging_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(DISCHRG);
/* Workaround NMC bug: both charging and discharging set to 1 */
if(STATUS_BIT(CHRG)) {
@ -140,9 +157,9 @@ static const char *discharging_info(const char *val)
return NULL;
}
static const char *charging_info(const char *val)
static const char *charging_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(CHRG);
} else {
STATUS_CLR(CHRG);
@ -151,9 +168,9 @@ static const char *charging_info(const char *val)
return NULL;
}
static const char *lowbatt_info(const char *val)
static const char *lowbatt_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(LOWBATT);
} else {
STATUS_CLR(LOWBATT);
@ -162,9 +179,9 @@ static const char *lowbatt_info(const char *val)
return NULL;
}
static const char *overload_info(const char *val)
static const char *overload_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(OVERLOAD);
} else {
STATUS_CLR(OVERLOAD);
@ -173,9 +190,9 @@ static const char *overload_info(const char *val)
return NULL;
}
static const char *replacebatt_info(const char *val)
static const char *replacebatt_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(REPLACEBATT);
} else {
STATUS_CLR(REPLACEBATT);
@ -184,9 +201,9 @@ static const char *replacebatt_info(const char *val)
return NULL;
}
static const char *trim_info(const char *val)
static const char *trim_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(TRIM);
} else {
STATUS_CLR(TRIM);
@ -195,9 +212,9 @@ static const char *trim_info(const char *val)
return NULL;
}
static const char *boost_info(const char *val)
static const char *boost_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(BOOST);
} else {
STATUS_CLR(BOOST);
@ -206,9 +223,9 @@ static const char *boost_info(const char *val)
return NULL;
}
static const char *bypass_aut_info(const char *val)
static const char *bypass_aut_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(BYPASSAUTO);
} else {
STATUS_CLR(BYPASSAUTO);
@ -217,9 +234,9 @@ static const char *bypass_aut_info(const char *val)
return NULL;
}
static const char *bypass_man_info(const char *val)
static const char *bypass_man_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(BYPASSMAN);
} else {
STATUS_CLR(BYPASSMAN);
@ -228,9 +245,9 @@ static const char *bypass_man_info(const char *val)
return NULL;
}
static const char *off_info(const char *val)
static const char *off_info(const char *arg_val)
{
if (val[0] == '0') {
if (arg_val[0] == '0') {
STATUS_SET(OFF);
} else {
STATUS_CLR(OFF);
@ -242,9 +259,9 @@ static const char *off_info(const char *val)
/* note: this value is reverted (0=set, 1=not set). We report "battery
not installed" rather than "battery installed", so that devices
that don't implement this variable have a battery by default */
static const char *nobattery_info(const char *val)
static const char *nobattery_info(const char *arg_val)
{
if (val[0] == '0') {
if (arg_val[0] == '0') {
STATUS_SET(NOBATTERY);
} else {
STATUS_CLR(NOBATTERY);
@ -253,9 +270,9 @@ static const char *nobattery_info(const char *val)
return NULL;
}
static const char *fanfail_info(const char *val)
static const char *fanfail_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(FANFAIL);
} else {
STATUS_CLR(FANFAIL);
@ -265,9 +282,9 @@ static const char *fanfail_info(const char *val)
}
#if 0
static const char *shutdownimm_info(const char *val)
static const char *shutdownimm_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(SHUTDOWNIMM);
} else {
STATUS_CLR(SHUTDOWNIMM);
@ -277,9 +294,9 @@ static const char *shutdownimm_info(const char *val)
}
#endif
static const char *overheat_info(const char *val)
static const char *overheat_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(OVERHEAT);
} else {
STATUS_CLR(OVERHEAT);
@ -288,9 +305,9 @@ static const char *overheat_info(const char *val)
return NULL;
}
static const char *commfault_info(const char *val)
static const char *commfault_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(COMMFAULT);
} else {
STATUS_CLR(COMMFAULT);
@ -299,9 +316,9 @@ static const char *commfault_info(const char *val)
return NULL;
}
static const char *internalfailure_info(const char *val)
static const char *internalfailure_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(INTERNALFAULT);
} else {
STATUS_CLR(INTERNALFAULT);
@ -310,9 +327,9 @@ static const char *internalfailure_info(const char *val)
return NULL;
}
static const char *battvoltlo_info(const char *val)
static const char *battvoltlo_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(BATTVOLTLO);
} else {
STATUS_CLR(BATTVOLTLO);
@ -321,9 +338,9 @@ static const char *battvoltlo_info(const char *val)
return NULL;
}
static const char *battvolthi_info(const char *val)
static const char *battvolthi_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(BATTVOLTHI);
} else {
STATUS_CLR(BATTVOLTHI);
@ -332,9 +349,9 @@ static const char *battvolthi_info(const char *val)
return NULL;
}
static const char *chargerfail_info(const char *val)
static const char *chargerfail_info(const char *arg_val)
{
if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) {
if ((arg_val[0] == '1') || !strncasecmp(arg_val, "Yes", 3)) {
STATUS_SET(CHARGERFAIL);
} else {
STATUS_CLR(CHARGERFAIL);
@ -343,9 +360,9 @@ static const char *chargerfail_info(const char *val)
return NULL;
}
static const char *vrange_info(const char *val)
static const char *vrange_info(const char *arg_val)
{
if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) {
if ((arg_val[0] == '1') || !strncasecmp(arg_val, "Yes", 3)) {
STATUS_SET(VRANGE);
} else {
STATUS_CLR(VRANGE);
@ -354,9 +371,9 @@ static const char *vrange_info(const char *val)
return NULL;
}
static const char *frange_info(const char *val)
static const char *frange_info(const char *arg_val)
{
if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) {
if ((arg_val[0] == '1') || !strncasecmp(arg_val, "Yes", 3)) {
STATUS_SET(FRANGE);
} else {
STATUS_CLR(FRANGE);
@ -365,9 +382,9 @@ static const char *frange_info(const char *val)
return NULL;
}
static const char *fuse_fault_info(const char *val)
static const char *fuse_fault_info(const char *arg_val)
{
if (val[0] == '1') {
if (arg_val[0] == '1') {
STATUS_SET(FUSEFAULT);
} else {
STATUS_CLR(FUSEFAULT);
@ -376,88 +393,112 @@ static const char *fuse_fault_info(const char *val)
return NULL;
}
static const char *yes_no_info(const char *val)
static const char *yes_no_info(const char *arg_val)
{
switch(val[0])
switch(arg_val[0])
{
case '1':
return "yes";
case '0':
return "no";
default:
upsdebugx(2, "%s: unexpected value [%s]", __func__, val);
upsdebugx(2, "%s: unexpected value [%s]", __func__, arg_val);
return "<unknown>";
}
}
static const char *on_off_info(const char *val)
static const char *on_off_info(const char *arg_val)
{
switch(val[0])
switch(arg_val[0])
{
case '1':
return "on";
case '0':
return "off";
default:
upsdebugx(2, "%s: unexpected value [%s]", __func__, val);
upsdebugx(2, "%s: unexpected value [%s]", __func__, arg_val);
return "<unknown>";
}
}
static const char *convert_deci(const char *val)
static const char *convert_deci(const char *arg_val)
{
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.1f", 0.1 * (float)atoi(val));
/* Note: this routine was needed for original MGE devices, before the company
* was bought out and split in 2007 between Eaton (1ph devices) and Schneider
* (3ph devices). Those firmwares back when the driver was written apparently
* served 10x the measured values. Not sure if any such units are in service
* now (with same FW, and with no upgrade path). Reign of XML/PDC is waning.
* For currently known NetXML servers, the value served is good without more
* conversions. If older devices pop up in the field, we can add an estimation
* by e.g. reported voltage and amps (to be an order of magnitude for power).
* Alternately we can look at model names and/or firmware versions or release
* dates, if we get those and if we know enough to map them to either logic. */
return mge_scratch_buf;
if (testvar("do_convert_deci")) {
/* Old code for old devices: */
if (mge_report_deprecation__convert_deci) {
upslogx(LOG_NOTICE, "%s() is now deprecated, so values from XML are normally not decimated. This driver instance has however configured do_convert_deci in your ups.conf, so this behavior for old MGE NetXML-capable devices is preserved.", __func__);
mge_report_deprecation__convert_deci = 0;
}
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.1f", 0.1 * (float)(atoi(arg_val)));
return mge_scratch_buf;
}
if (mge_report_deprecation__convert_deci) {
upslogx(LOG_NOTICE, "%s() is now deprecated, so values from XML are not decimated. If you happen to have an old MGE NetXML-capable device that now shows measurements 10x too big, and a firmware update does not solve this, please inform NUT devs via the issue tracker at %s with details about your hardware and firmware versions. Also try to enable do_convert_deci in your ups.conf", __func__, PACKAGE_BUGREPORT );
mge_report_deprecation__convert_deci = 0;
}
upsdebugx(5, "%s() is now deprecated, so value '%s' is not decimated. If this change broke your setup, please see details logged above.", __func__, arg_val);
return arg_val;
}
/* Ignore a zero value if the UPS is not switched off */
static const char *ignore_if_zero(const char *val)
static const char *ignore_if_zero(const char *arg_val)
{
if (atoi(val) == 0) {
if (atoi(arg_val) == 0) {
return NULL;
}
return convert_deci(val);
return convert_deci(arg_val);
}
/* Set the 'ups.date' from the combined value
* (ex. 2008/03/01 15:23:26) and return the time */
static const char *split_date_time(const char *val)
static const char *split_date_time(const char *arg_val)
{
char *last = NULL;
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", val);
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", arg_val);
dstate_setinfo("ups.date", "%s", strtok_r(mge_scratch_buf, " -", &last));
return strtok_r(NULL, " ", &last);
}
static const char *url_convert(const char *val)
static const char *url_convert(const char *arg_val)
{
char buf[256], *last = NULL;
snprintf(buf, sizeof(buf), "%s", val);
snprintf(buf, sizeof(buf), "%s", arg_val);
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "/%s", strtok_r(buf, " \r\n\t", &last));
return mge_scratch_buf;
}
static const char *mge_battery_capacity(const char *val)
static const char *mge_battery_capacity(const char *arg_val)
{
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)atoi(val) / 3600);
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)(atoi(arg_val)) / 3600.0);
return mge_scratch_buf;
}
static const char *mge_powerfactor_conversion(const char *val)
static const char *mge_powerfactor_conversion(const char *arg_val)
{
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)atoi(val) / 100);
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)(atoi(arg_val)) / 100.0);
return mge_scratch_buf;
}
static const char *mge_beeper_info(const char *val)
static const char *mge_beeper_info(const char *arg_val)
{
switch (atoi(val))
switch (atoi(arg_val))
{
case 1:
return "disabled";
@ -469,9 +510,9 @@ static const char *mge_beeper_info(const char *val)
return NULL;
}
static const char *mge_upstype_conversion(const char *val)
static const char *mge_upstype_conversion(const char *arg_val)
{
switch (atoi(val))
switch (atoi(arg_val))
{
case 1:
return "offline / line interactive";
@ -487,9 +528,9 @@ static const char *mge_upstype_conversion(const char *val)
return NULL;
}
static const char *mge_sensitivity_info(const char *val)
static const char *mge_sensitivity_info(const char *arg_val)
{
switch (atoi(val))
switch (atoi(arg_val))
{
case 0:
return "normal";
@ -501,9 +542,10 @@ static const char *mge_sensitivity_info(const char *val)
return NULL;
}
static const char *mge_test_result_info(const char *val)
static const char *mge_test_result_info(const char *arg_val)
{
switch (atoi(val))
STATUS_CLR(CAL);
switch (atoi(arg_val))
{
case 1:
return "done and passed";
@ -514,6 +556,7 @@ static const char *mge_test_result_info(const char *val)
case 4:
return "aborted";
case 5:
STATUS_SET(CAL);
return "in progress";
case 6:
return "no test initiated";
@ -523,12 +566,31 @@ static const char *mge_test_result_info(const char *val)
return NULL;
}
static const char *mge_ambient_info(const char *val)
static const char *mge_ambient_info(const char *arg_val)
{
switch (mge_ambient_value)
{
case 1:
return val;
return arg_val;
default:
return NULL;
}
}
static const char *mge_drycontact_info(const char *arg_val)
{
/* these values should theoretically be obtained through
* Environment.Input[1].State[x].Description
* Examples:
* <OBJECT name="Environment.Input[1].State[0].Description">open</OBJECT>
* <OBJECT name="Environment.Input[1].State[1].Description">closed</OBJECT>
*/
switch (atoi(arg_val))
{
case 0:
return "opened";
case 1:
return "closed";
default:
return NULL;
}
@ -552,9 +614,9 @@ static const char *mge_timer_shutdown(const char *delay_before_shutoff)
return val;
}
static const char *mge_shutdown_imminent(const char *val)
static const char *mge_shutdown_imminent(const char *arg_val)
{
const int shutdown_delay = atoi(val);
const int shutdown_delay = atoi(arg_val);
/* shutdown is already managed by mge_timer_shutdown, give up */
if(mge_shutdown_pending) {
@ -563,7 +625,7 @@ static const char *mge_shutdown_imminent(const char *val)
/* We may have "NONE" or "-1" or ?? as value
* We also double check both the string and numeric values to be zero!*/
if ((val) && (val[0] == '0') && (shutdown_delay == 0)) {
if ((arg_val) && (arg_val[0] == '0') && (shutdown_delay == 0)) {
STATUS_SET(SHUTDOWNIMM);
} else {
STATUS_CLR(SHUTDOWNIMM);
@ -885,12 +947,10 @@ static xml_info_t mge_xml2nut[] = {
{ "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", 0, 0, NULL },
{ "battery.runtime.low", ST_FLAG_RW, 0, "System.RunTimeToEmptyLimit", 0, 0, NULL },
{ "battery.temperature", 0, 0, "UPS.BatterySystem.Battery.Temperature", 0, 0, NULL },
{ "battery.packs.external", 0, 0, "UPS.BatterySystem.Battery.Count", 0, 0, NULL },
{ "battery.type", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iDeviceChemistry", 0, 0, NULL },
{ "battery.type", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iDeviceChemistery", 0, 0, NULL }, /* [sic] */
{ "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", 0, 0, NULL },
{ "battery.voltage.nominal", ST_FLAG_STATIC, 0, "UPS.BatterySystem.ConfigVoltage", 0, 0, NULL },
{ "battery.voltage.nominal", ST_FLAG_STATIC, 0, "UPS.PowerSummary.ConfigVoltage", 0, 0, NULL }, /* mge_battery_voltage_nominal */
{ "battery.current", 0, 0, "UPS.PowerSummary.Current", 0, 0, NULL },
{ "battery.protection", 0, 0, "UPS.BatterySystem.Battery.DeepDischargeProtection", 0, 0, yes_no_info },
{ "battery.energysave", 0, 0, "UPS.PowerConverter.Input[3].EnergySaving", 0, 0, yes_no_info },
@ -911,7 +971,7 @@ static xml_info_t mge_xml2nut[] = {
{ "ups.delay.shutdown", ST_FLAG_RW, 0, "System.ShutdownDuration", 0, 0, NULL },
{ "ups.timer.start", ST_FLAG_RW, 0, "UPS.PowerSummary.DelayBeforeStartup", 0, 0, NULL},
{ "ups.timer.shutdown", ST_FLAG_RW, 0, "UPS.PowerSummary.DelayBeforeShutdown", 0, 0, mge_timer_shutdown },
/* Catch shutdown imminent criteria, keep it after
/* Catch shutdown imminent criteria, keep it after
UPS.PowerSummary.DelayBeforeShutdown managment */
{ NULL, 0, 0, "System.RunTimeToShutdown", 0, 0, mge_shutdown_imminent },
{ "ups.timer.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", 0, 0, NULL },
@ -920,15 +980,15 @@ static xml_info_t mge_xml2nut[] = {
{ "ups.beeper.status", 0 ,0, "UPS.BatterySystem.Battery.AudibleAlarmControl", 0, 0, mge_beeper_info },
{ "ups.beeper.status", 0 ,0, "UPS.PowerSummary.AudibleAlarmControl", 0, 0, mge_beeper_info },
{ "ups.temperature", 0, 0, "UPS.PowerSummary.Temperature", 0, 0, NULL },
{ "ups.power", 0, 0, "UPS.PowerConverter.Output.ApparentPower", 0, 0, NULL },
{ "ups.L1.power", 0, 0, "UPS.PowerConverter.Output.Phase[1].ApparentPower", 0, 0, ignore_if_zero },
{ "ups.L2.power", 0, 0, "UPS.PowerConverter.Output.Phase[2].ApparentPower", 0, 0, ignore_if_zero },
{ "ups.L3.power", 0, 0, "UPS.PowerConverter.Output.Phase[3].ApparentPower", 0, 0, ignore_if_zero },
{ "ups.power.nominal", ST_FLAG_STATIC, 0, "UPS.Flow[4].ConfigApparentPower", 0, 0, NULL },
{ "ups.power", 0, 0, "UPS.PowerConverter.Output.ApparentPower", 0, 0, NULL },
{ "ups.L1.power", 0, 0, "UPS.PowerConverter.Output.Phase[1].ApparentPower", 0, 0, NULL },
{ "ups.L2.power", 0, 0, "UPS.PowerConverter.Output.Phase[2].ApparentPower", 0, 0, NULL },
{ "ups.L3.power", 0, 0, "UPS.PowerConverter.Output.Phase[3].ApparentPower", 0, 0, NULL },
{ "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", 0, 0, NULL },
{ "ups.L1.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[1].ActivePower", 0, 0, ignore_if_zero },
{ "ups.L2.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[2].ActivePower", 0, 0, ignore_if_zero },
{ "ups.L3.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[3].ActivePower", 0, 0, ignore_if_zero },
{ "ups.L1.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[1].ActivePower", 0, 0, NULL },
{ "ups.L2.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[2].ActivePower", 0, 0, NULL },
{ "ups.L3.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[3].ActivePower", 0, 0, NULL },
{ "ups.realpower.nominal", ST_FLAG_STATIC, 0, "UPS.Flow[4].ConfigActivePower", 0, 0, NULL },
{ "ups.start.auto", 0, 0, "UPS.PowerConverter.Input[1].AutomaticRestart", 0, 0, yes_no_info },
{ "ups.start.battery", 0, 0, "UPS.PowerConverter.Input[3].StartOnBattery", 0, 0, yes_no_info },
@ -1018,6 +1078,8 @@ static xml_info_t mge_xml2nut[] = {
{ "ambient.temperature.low", ST_FLAG_RW, 0, "Environment.Temperature.LowThreshold", 0, 0, NULL },
{ "ambient.temperature.maximum", 0, 0, "Environment.PresentStatus.HighTemperature", 0, 0, mge_ambient_info },
{ "ambient.temperature.minimum", 0, 0, "Environment.PresentStatus.LowTemperature", 0, 0, mge_ambient_info },
{ "ambient.contacts.1.status", 0, 0, "Environment.Input[1].PresentStatus.State", 0, 0, mge_drycontact_info },
{ "ambient.contacts.2.status", 0, 0, "Environment.Input[2].PresentStatus.State", 0, 0, mge_drycontact_info },
/* Outlet page (using MGE UPS SYSTEMS - PowerShare technology) */
{ "outlet.id", 0, 0, "UPS.OutletSystem.Outlet[1].OutletID", 0, 0, NULL },
@ -1077,6 +1139,8 @@ static xml_info_t mge_xml2nut[] = {
static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, const char *name, const char **atts)
{
int state = _UNEXPECTED;
NUT_UNUSED_VARIABLE(userdata);
NUT_UNUSED_VARIABLE(nspace);
switch(parent)
{
@ -1085,6 +1149,7 @@ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, c
/* name="Network Management Card" type="Mosaic M" version="BA" */
/* name="Network Management Card" type="Transverse" version="GB (SN 49EH29101)" */
/* name="Monitored ePDU" type="Monitored ePDU" version="Version Upgrade" */
/* name="PDU Network Management Card" type="SCOB" version="02.00.0036" signature="34008876" protocol="XML.V4" */
int i;
for (i = 0; atts[i] && atts[i+1]; i += 2) {
if (!strcasecmp(atts[i], "name")) {
@ -1108,6 +1173,13 @@ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, c
}
dstate_setinfo("ups.firmware.aux", "%s", val);
}
/* netxml-ups currently only supports XML version 3 (for UPS),
* and not version 4 (for UPS and PDU)! */
if (!strcasecmp(atts[i], "protocol")) {
if (!strcasecmp(atts[i+1], "XML.V4")) {
fatalx(EXIT_FAILURE, "XML v4 protocol is not supported!");
}
}
}
state = PRODUCT_INFO;
break;
@ -1310,8 +1382,12 @@ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, c
state = XC_GENERAL;
break;
}
/* FIXME? Is the fall-through to handling "GENERAL" intended?
* was so in legacy code before the goto below... */
goto fallthrough_case_general;
case XC_GENERAL:
fallthrough_case_general:
if (!strcasecmp(name, "STARTUP")) {
/* config="CENTRALIZED" */
state = XC_STARTUP;
@ -1345,6 +1421,8 @@ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, c
/* Character data callback; may return non-zero to abort the parse. */
static int mge_xml_cdata_cb(void *userdata, int state, const char *cdata, size_t len)
{
NUT_UNUSED_VARIABLE(userdata);
/* skip empty lines */
if ((len == 1) && (cdata[0] == '\n')) {
upsdebugx(3, "%s: cdata ignored (state = %d)", __func__, state);
@ -1373,6 +1451,8 @@ static int mge_xml_endelm_cb(void *userdata, int state, const char *nspace, cons
{
xml_info_t *info;
const char *value;
NUT_UNUSED_VARIABLE(userdata);
NUT_UNUSED_VARIABLE(nspace);
/* ignore objects for which no value was set */
if (strlen(val) == 0) {
@ -1400,6 +1480,7 @@ static int mge_xml_endelm_cb(void *userdata, int state, const char *nspace, cons
if (info->convert) {
value = info->convert(val);
upsdebugx(4, "-> XML variable %s [%s] which maps to NUT variable %s was converted to value %s for the NUT driver state", var, val, info->nutname, value);
} else {
value = val;
}
@ -1413,6 +1494,25 @@ static int mge_xml_endelm_cb(void *userdata, int state, const char *nspace, cons
upsdebugx(3, "-> XML variable %s [%s] doesn't map to any NUT variable", var, val);
break;
case PI_GET_OBJECT:
case GET_OBJECT:
/* We've just got a snapshot of all runtime data, saved well into
* dstate's already, so can estimate missing values if needed. */
/* For phase setup, we assume it does not change during run-time.
* Essentially this means that once we've detected it is N-phase,
* it stays this way for the rest of the driver run/life-time. */
/* To change this behavior just flip the maychange flag to "1" */
dstate_detect_phasecount("input.", 1,
&inited_phaseinfo_in, &num_inphases, 0);
dstate_detect_phasecount("input.bypass.", 1,
&inited_phaseinfo_bypass, &num_bypassphases, 0);
dstate_detect_phasecount("output.", 1,
&inited_phaseinfo_out, &num_outphases, 0);
break;
}
return 0;